美团 Cat 源码文章
注意的地方:1,Cat使用的是plexus作为底层容器,又在此容器基础之上封装了一个容器叫org.unidal.framework。一些Web请求应该都是通过框架接收的。2,有一些Dao是在GenerateSource后才能生成的。透过CAT,来看分布式实时监控系统的设计与实现:Cat的访谈介绍官方文档:先把官方文档读一下,看看已经提供了什么。基于Cat的分布式调用追踪:如何进行埋点的例
注意的地方:
1,Cat使用的是plexus
作为底层容器,又在此容器基础之上封装了一个容器叫org.unidal.framework
。一些Web请求应该都是通过框架接收的。
2,有一些Dao是在GenerateSource后才能生成的。
透过CAT,来看分布式实时监控系统的设计与实现:Cat的访谈介绍
官方文档:先把官方文档读一下,看看已经提供了什么。
基于Cat的分布式调用追踪:如何进行埋点的例子。
基于Cat的分布式调用追踪:Client端埋点代码的分析
大众点评的实时监控系统分析(一):Client端类介绍,非常好
CAT_source_analyze:Client端和Server端代码的分析
大众点评Cat–架构分析:总体架构分析,不错
大众点评Cat–Server模块架构分析:Server端架构分析,非常好
临时记录
1,Client的分析过程
程序入口 与 什么时候写数据到Server
手动埋点的Sample代码如下:
public @ResponseBody String test() {
Transaction t = Cat.newTransaction("MY-TRANSACTION","test in TransactionTest");
try{
Cat.logEvent("EVENT-TYPE-1","EVENT-NAME-1");
// ....
}catch(Exception e){
Cat.logError(e);
t.setStatus(e);
}finally {
t.setStatus(Transaction.SUCCESS);
t.complete();
}
return "trasaction test!";
}
1,程序入口:
从上面代码来看,程序入口是
Cat.newTransaction(type, name)
。
2,什么时候写数据到Server:
写数据是由MessageManager#flush
方法进行的。都在什么时候调用了这个方法呢?
- Trasaction.complete()
- Metric/Event/HeartBeat/Trace.complete()
在这里方法里最终都调用了
flash
方法,但过程都不一样。
1,newTransaction
流程:
- 看是否有
context
,没有就创建一个。 - 新建一个
Transaction
。 - 使用
MessageManager.start
方法,处理这个transaction
。start
作用:把这个transaction
加入到MessageTree
里面。所有的消息都是保存MessageTree
里面,然后发送。 - 返回新创建的
Trasaction
。
在这里,重要的地方是MessageManager.start
方法,这个方法的最关键一行代码是:ctx.start(transaction, forked);
。
Context是一个内部类,作者就是把
MessageTree
和对MessageTree的操作
都包装到这个类里了,可能是为了代码看起来更简洁、清晰吧。(这个Context是MessageManager里的内部类,Cat类里还有一个Context接口,不要误解了)。
下面分析这个方法:
如果Context的stack属性(这个属性类型是Stack)不是空
- 把stack里保存的
Transaction
取出来。 把当前的
Transaction
,加到取出来的Transaction
里面。加的过程如下:- 计算时间 或 长度条件,如果需要发送到Server,就发送到Server里(truncateAndFlush)
- 把当前Transaction加到父Transaction里面
- Context.length属性++
- 把stack里保存的
如果stack属性(这个属性类型是Stack)是空,就把当前这个
Transaction
加到MessageTree
里面。- 最后,把当前的Transaction加入到
stack
里面。
在Context里面有个stack
属性,Context是怎么使用这个属性的呢?
对于Transaction,当
stack
为空时,把Transaction
加到MessageTree
中(setMessage)。如果不为空,把stack
的最上面的数据拿出来(看代码,最上面的一定是Transaction)作为父Transactin
,把当前Transaction
放到父Transaction
里面。最后会把当前Transaction加到stack里面。对于Event,当
stack
为空时,把Event加到MessageTree
,把MessageTree
进行Flush(发送到Server)。如果不为空,把stack
的最上面的数据拿出来(看代码,最上面的一定是Transaction),放到Transaction
里面。
从上面的比较来说,Transaction
一定会被放到stack
里面;而Event
的话,如果stack
为空就不放到stack
里面,直接Flush。Transaction一定会放到stack里面,而Event是不放stack
里面(在Stack不为空时,不放到放到Transaction
里面)
todo event里的,flush是干什么用的,为什么flush完还要放到transaction里面。
2,newEvent
流程:
- 看是否有
context
,没有就创建一个。 - 新建一个
Event
。 - 返回新创建的
Trasaction
。
3,在创建完Traction后,再嵌套
创建Transaction,流程是怎么做的?
因为Transaction(只有DefaultTransaction可以)可以嵌套,所以在DefaultTransaction里面有一个m_children参数,这个参数是一个List。所有嵌套在当前Transaction里面的Transaction,都放到这个List中。而且,还将这个Transaction的standalone属性(这个属性应该是代表有没有嵌套),设计成false。
嵌套时,调用的方法为newTransaction(Transaction parent, String type, String name)
,第一个参数是父Transaction
的实例。
嵌套调用时,只是把新的Transaction
放到另一个Transaction
的List里面,没有放到MessageTree里面。
4,在创建完Traction后,再创建嵌套
Event,流程是怎么做的?
基本上和嵌套Transaction一样。
5,Transaction的complete方法是如何做的?
- 判断是否已经调用过complete方法了了。如果已经完成了,就生成个Event 并且 加到当前Transaction里面。这个Event代表着,Transaction的Complete方法已经被调用了多次。
- 如果没有调用过complete方法,做以下操作:
- 计算Transaction从开始到结束的时间
- 把Transaction的complete属性,设置成已完成
- 调用MessageManager.end方法
在这里,最后一步调用MessageManager.end方法
是关键,我们下面分析一下这个方法。
这个方法流程如下:
- 取得context。
- 如果 context不为空 并且 当前transaction是Standalone状态,就调用context.end方法。
这里说明一下,Context是什么呢?
Context是一个内部类,作者就是把MessageTree
和对MessageTree的操作
都包装到这个类里了,可能是为了代码看起来更简洁、清晰吧。(这个Context是MessageManager里的内部类,Cat类里还有一个Context接口,不要误解了)。
是Standalone状态
表示什么呢?
表示这个Transaction不是一个嵌套的Transaction,或者是一个“已经嵌套关闭”的嵌套Transaction。
todo 这里还没有分析 Context.end 方法。
小知识点:
- 每个 transaction 里面都有一个
MessageManager
,用来控制消息的写入和发送。(同理每个Event等里面,也是有一个MessageManager
的) - 在
MessageManager
里面和Cat
里面,各有一个Context
,用来保存一些用的环境变量,还是其它什么别的? - AbstractMessage里的
completed
,应该是用来判断Tranaction
是否是完成状态。其它Metric/Event/HeartBeat/Trace
的话,add到MessageTree
后,直接就调用complete
方法,把这个标志位设置成完成
状态。
问题:
- MessageIdFactory是做什么的?
- 单独创建一个Transaction的逻辑是什么?
已解决问题:
- 在创建完Traction后,再创建Event,流程是怎么做的?
- 在创建完Traction后,再创建Transaction,流程是怎么做的?
- Transaction嵌套是怎么做的?
分析过程中的临时记录
Transaction transaction = Cat.newTransaction(provide/consumer,OrderService.placeOrder);
// 如果是consumer
createConsumerCross(URL url,Transaction transaction)
send:
PigeonCall.app event (user-service)(被调用的 service 的名字)
PigeonCall.server event (10.172.129.250)(被调用的 service 的IP。这个方法被调用时,还没有真正的调用远程服务)
PigeonCall.port event (7070)
add:
OrderService.placeOrder
logRemoteCallClient
send:
event: RemoteCall childid
向context里设置:ROOT,PARENT,CHILD
(childid是新new的,每回调用其它service之前,都会新new一个childid)
// 如果是 provider
createProviderCross(url,transaction);
send:
PigeonService.app event
PigeonService.client event
add:
OrderService.placeOrder
PigeonService.app event (10.44.32.16:52070)(调用这个Service的服务的IP,有可能是“其它Service”或“API”)
PigeonService.client event (10.31.153.29)(本机IP)
Cat.logRemoteCallServer(context);
设置:MessageTree的 messageId,parentId,rootId
// 调用被拦截接口
result = invoker.invoke(invocation);
// 最后
transaction.complete();
DefaultReportManager#storeDatabase:向数据库里保存两条记录,一个是report,另一个是reportContent
DefaultNativeBuilder:写日志
LocalReportBucket:可以读本地文件系统中的日志文件
Period
DefaultMessageAnalyzerManager.getAnalyzer
analyzer.initialize
loadReports();
CrossAnalyzer
m_reportManager.loadHourlyReports
m_bucketManager.getReportBucket
在CAT的URL后面加上参数 forceDownload=xml ,可以看到当前页面的数据模型
com.dianping.cat.report.page.cross.Handler 是画面的处理器
getHourlyReport(Payload payload) 是处理方法,Payload是URL后面的参数DTO
问题:
1,Cat是如何读出来数据的。
2,ReportContent表里的Content有没有用。
3,
更多推荐
所有评论(0)