注意的地方:
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流程:

  1. 看是否有context,没有就创建一个。
  2. 新建一个Transaction
  3. 使用MessageManager.start方法,处理这个transactionstart作用:把这个transaction加入到MessageTree里面。所有的消息都是保存MessageTree里面,然后发送。
  4. 返回新创建的Trasaction

在这里,重要的地方是MessageManager.start方法,这个方法的最关键一行代码是:ctx.start(transaction, forked);

Context是一个内部类,作者就是把MessageTree对MessageTree的操作都包装到这个类里了,可能是为了代码看起来更简洁、清晰吧。(这个Context是MessageManager里的内部类,Cat类里还有一个Context接口,不要误解了)。

下面分析这个方法:

  1. 如果Context的stack属性(这个属性类型是Stack)不是空

    • 把stack里保存的Transaction取出来。
    • 把当前的Transaction,加到取出来的Transaction里面。加的过程如下:

      1. 计算时间 或 长度条件,如果需要发送到Server,就发送到Server里(truncateAndFlush)
      2. 把当前Transaction加到父Transaction里面
      3. Context.length属性++
  2. 如果stack属性(这个属性类型是Stack)是空,就把当前这个Transaction加到MessageTree里面。

  3. 最后,把当前的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流程:

  1. 看是否有context,没有就创建一个。
  2. 新建一个Event
  3. 返回新创建的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方法是如何做的?

  1. 判断是否已经调用过complete方法了了。如果已经完成了,就生成个Event 并且 加到当前Transaction里面。这个Event代表着,Transaction的Complete方法已经被调用了多次。
  2. 如果没有调用过complete方法,做以下操作:
    • 计算Transaction从开始到结束的时间
    • 把Transaction的complete属性,设置成已完成
    • 调用MessageManager.end方法

在这里,最后一步调用MessageManager.end方法是关键,我们下面分析一下这个方法。
这个方法流程如下:

  1. 取得context。
  2. 如果 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,

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐