深入详解美团点评CAT跨语言服务监控(七)消息分析器与报表(二)

本文深入解析美团点评的CAT(Catapolt Application Tracking)跨语言服务监控中的调用链分析和报表。CAT通过CrossAnalyzer追踪服务之间的调用,包括访问量、错误量、响应时间和QPS等。文章详细介绍了客户端埋点、服务端分析逻辑,以及监控数据库和缓存的StorageAnalyzer。此外,还提及了StateAnalyzer、HeartbeatAnalyzer和DumpAnalyzer的功能和工作原理。
摘要由CSDN通过智能技术生成

上一篇:CAT跨语言服务链监控(六)消息分析器与报表(一)            下一篇:CAT跨语言服务链监控(八)报表持久化

CrossAnalyzer-调用链分析 

    在分布式环境中,应用是运行在独立的进程中的,有可能是不同的机器,或者不同的服务器进程。那么他们如果想要彼此联系在一起,形成一个调用链,在Cat中,CrossAnalyzer会统计不同服务之间调用的情况,包括服务的访问量,错误量,响应时间,QPS等,这里的服务主要指的是 RPC 服务,在微服务监控中,这是核心。

    在讲 CrossAnalyzer 的处理逻辑之前,我们先看下客户端的埋点的一个模拟情况。

    一般情况下不同服务会通过几个ID进行串联。这种串联的模式,基本上都是一样的。在Cat中,我们需要3个ID:

  • RootId,用于标识唯一的一个调用链
  • ParentId,父Id是谁?谁在调用我
  • ChildId,我在调用谁?

      那么我们如何传递这些ID?Cat为我们提供了一个内部接口 Cat.Context,但是我们需要自己实现Context,在下面代码中我们首先在before函数中实现了Context 上下文,然后在rpcClient中开启消息事务,并调用 Cat.logRemoteCallClient(context) 去填充Context的这3个MessageID。当然,该函数还记录了一个RemoteCall类型的Event消息。

     随后我们用rpcService函数中开启新线程模拟远程RPC服务,并将context上传到 RPC 服务器,在真实环境中,Context是需要跨进程网络传输,因此需要实现序列化接口。

    在rpcService中,我们会调用 Cat.logRemoteCallServer(context) 将从rpcClient传过来的Context设置到自己的 Transaction 当中。

    随着业务处理逻辑的结束, rpcServer 和 rpcClient 都会分别将自己的消息树上传到CAT服务器分析。

    需要注意的是,Service的client和app需要和Call的server以及app对应上,要不然图表是分析不出东西的!

@RunWith(JUnit4.class)
public class AppSimulator extends CatTestCase {
    public Map<String, String> maps = new HashMap<String, String>();

    public Cat.Context context;

    @Before
    public void before() {
        context = new Cat.Context() {
            @Override
            public void addProperty(String key, String value) { maps.put(key, value); }

            @Override
            public String getProperty(String key) { return maps.get(key); }
        };
    }

    @Test
    public void simulateHierarchyTransaction() throws Exception {
            ...
            //RPC调用开始
            rpcClient();
            rpcClient2();
            ...
    }

    protected void rpcClient() {
        //客户端埋点,Domain为RpcClient,调用服务端提供的Echo服务
        Transaction parent = Cat.newTransaction("Call", "CallServiceEcho");
        Cat.getManager().getThreadLocalMessageTree().setDomain("RpcClient");

        Cat.logEvent("Call.server","localhost");
        Cat.logEvent("Call.app","RpcService");
        Cat.logEvent("Call.port","8888");
        Cat.logRemoteCallClient(context, "RpcClient");

        //开启新线程模拟远程RPC服务,将context上传到 RPC 服务器
        rpcService(context);

        parent.complete();
    }
    
    protected void rpcClient2() {
        ...
        //模拟另外一个RpcClient调用Echo服务
        rpcService(context, "RpcClient2");
        ...
    }
    
    protected void rpcService(final Cat.Context context, final String clientDomain) {
        Thread thread = new Thread() {
            @Override
            public void run() {
                //服务器埋点,Domain为 RpcService 提供Echo服务
                Transaction child = Cat.newTransaction("Service", "Echo");
                Cat.getManager().getThreadLocalMessageTree().setDomain("RpcService");

                Cat.logEvent("Service.client", localhost); //填客户端地址
                Cat.logEvent("Service.app", clientDomain);
                Cat.logRemoteCallServer(context);

                //to do your business

                child.complete();
            }
        };

        thread.start();

        try {
            thread.join();
        } catch (InterruptedException e) {
        }
    }
}

 

接下来我们看看CAT服务器端CrossAnalyzer的逻辑。

 

        我们依然会为每个周期时间内的每个Domain创建一张报表(CrossReport),然后不同的IP会分配不同的Local对象统计,每个IP又可能会接收来自不同Remote端的调用。

    由于这里一个完整的调用链会涉及多个端的多个消息树,我们首先会根据Transaction的类型来判断是RpcService还是RpcClient,如果Type等于PigeonService或Service则该消息来自RpcService,如果Type等于 PigeonCall或Call则来自RpcClient。

    先来看看RpcService端消息树的上报处理逻辑,CAT会调用 parsePigeonServerTransaction 函数去填充 CrossInfo 信息,CrossInfo包含的具体内容如下:

     localAddress : RpcService的IP地址

     remoteAddress : 服务调用者(RpcClient)的IP地址,由type="Service.client" 的Event子消息提供,注意,在处理RpcClient的上报时,我们会根据上报信息中的remoteAddress再次统计该RpcService数据,大家可能会疑惑这里是不是重复统计,事实上他们所处的视角是不一样的,前者是站在服务提供者的视角来统计我完成这次服务所耗费的时间、资源等,而后者则是站在RpcClient视角去统计自己从发出请求到得到结果所需的时长、资源等等,比如这中间就包含网络IO的消耗,这些在后续的报表中会有体现。

       app:客户端的Domain, 由type="Service.app"的Event子消息提供。

       remoteRole:固定为 Pigeon.Client , 表示远端角色为 Rpc 客户端。

       detailType: 固定为 PigeonService , 表示自己角色为 Rpc 服务端。

        最后,我们将用CrossInfo信息来更新报表(CrossReport),我们首先根据 localAddress 即 RpcService的 IP 找到或创建 Local对象,然后根据 remoteAddress+remoteRole 找到或创建 Remote 对象,然后统计服务的访问量,错误量,处理时间,QPS。

       RpcService提供不只一个服务,不同的服务我们按名字分别统计在不同的Name对象里,比如上面案例,RpcService提供的是Echo服务。

 

我们再来看看RpcClient端上报处理逻辑,CAT调用parsePigeonClientTransaction函数填充CrossInfo信息,具体如下:

localAddress : RpcClient的IP地址

remoteAddress :服务提供者(RpcService)的地址,由 type="Call.server" 的Event子消息提供。

app:服务提供者的Domain,由type="Call.app" 的Event子消息提供,在统计完RpcClient端数据之后,会通过该属性获取服务提供者的CrossInfo。从RpcClient的视角再次统计RpcService的数据。

port:客户端端口,由 type="Call.port" 的Event子消息提供。

remoteRole:固定为 Pigeon.Server, 表示远端角色为服务提供者。

detailType: 固定为 PigeonCall , 表示自己角色为服务调用者。

    然后,我们将用CrossInfo信息来更新报表(CrossReport),也是根据 localAddress 找到Local对象,然后根据 remoteAddress+remoteRole 找到 Remote 对象,进行统计。

 

    接着,我们通过convertCrossInfo函数利用RpcClient的CrossInfo信息去生成服务提供者的CrossInfo信息,这里实际上是为了从RpcClient的视角去统计服务提供者的报表!

public class CrossAnalyzer extends AbstractMessageAnalyzer<CrossReport> implements LogEnabled {
    private void processTransaction(CrossReport report, MessageTree tree, Transaction t) {
        CrossInfo crossInfo = parseCorssTransaction(t, tree);

        if (crossInfo != null &&
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值