`
qingwengang1
  • 浏览: 8992 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

基于Cat的分布式调用追踪

阅读更多

猿教程_-webapi教程-WebAPI教程

猿教程_-webapi教程-Web API概述

猿教程_-webapi教程-新建Web Api项目

猿教程_-webapi教程-测试Web API

猿教程_-webapi教程-Web API Controller

猿教程_-webapi教程-配置Web API

猿教程_-webapi教程-Web API路由

猿教程_-webapi教程-参数绑定

猿教程_-webapi教程-Action方法返回类型

猿教程_-webapi教程-Web API Request/Response 数据格式

猿教程_-webapi教程-媒体格式器

猿教程_-webapi教程-Web API过滤器

猿教程_-webapi教程-创建包含CRUD操作的Web API接口-第一部分

猿教程_-webapi教程-创建包含CRUD操作的Web API接口2:实现Get方法

猿教程_-webapi教程-创建包含CRUD操作的Web API接口3:实现Post方法

猿教程_-webapi教程-创建包含CRUD操作的Web API接口4:实现Put方法

猿教程_-webapi教程-创建包含CRUD操作的Web API接口5:实现Delete方法

使用Cat断断续续将近两周的时间,感觉它还算是很轻量级的。文档相对来说薄弱一些,没有太全面的官方文档(官方文档大多是介绍每个名词是什么意思,界面是什么意思,部署方面比较欠缺);但是好在有一个非常活跃的群,群里有很多经验丰富的高手,不会的问题基本都能得到解答。

下面就开始步入正题吧,本篇主要讲述一下如何利用Cat进行分布式的调用链追踪。

分布式开发基础

在最开始网站基本都是单节点的,由于业务逐渐发展,使用者开始增多,单节点已经无法支撑了。于是开始切分系统,把系统拆分成几个独立的模块,模块之间采用远程调用的方式进行通信。

那么远程调用是如何做到的呢?下面就用最古老的RMI的方式来举个例子吧!

RMI(Remote method invocation)是java从1.1就开始支持的功能,它支持跨进程间的方法调用。

大体上的原理可以理解为,服务端会持续监听一个端口。客户端通过proxy代理的方式远程调用服务端。即客户端会把方法的参数以字符串的的方式序列化传给服务端。服务端反序列化后调用本地的方法执行,执行结果再序列化返回给客户端。

服务端的代码可以参考如下:


interface IBusiness extends Remote{
    String echo(String message) throws RemoteException;
}
class BusinessImpl extends UnicastRemoteObject implements  IBusiness {
    publicBusinessImpl() throws RemoteException {}
    @Override
    public String echo(String message) throws RemoteException {
        return "hello,"+message;
    }
}
public class RpcServer {
    publicstaticvoidmain(String[] args) throws RemoteException, AlreadyBoundException, MalformedURLException {
        IBusiness business = new BusinessImpl();
        LocateRegistry.createRegistry(8888);
        Naming.bind("rmi://localhost:8888/Business",business);
        System.out.println("Hello, RMI Server!");
    }
}

客户端的代码如下:

IBusiness business = (IBusiness) Naming.lookup("rmi://localhost:8888/Business");
            business.echo("xingoo",ctx);

上面的例子就可以实现客户端跨进程调用的例子。

Cat监控

Cat的监控跟传统的APM产品差不多,模式都是相似的,需要一个agent在客户端进行埋点,然后把数据发送给服务端,服务端进行解析并存储。只要你埋点足够全,那么它是可以进行全面监控的。监控到的数据会首先按照某种规则进行消息的合并,合并成一个MessageTree,这个MessageTree会被放入BlockingQueue里面,这样就解决了多线程数据存储的问题。

队列会限制存储的MessageTree的个数,但是如果服务端挂掉,客户端也有可能因为堆积大量的心跳而导致内存溢出(心跳是Cat客户端自动向服务端发出的,里面包含了jvm本地磁盘IO等很多的内容,所以MesssageTree挺大的)。

因此数据在客户端的流程可以理解为:

Trasaction\Event-->MessageTree-->BlockingQueue-->netty发出网络流

即Transaction、Event等消息会先合并为消息树,以消息树为单位存储在内存中(并未进行本地持久化),专门有一个TcpSocketSender负责向外发送数据。

再说说服务端,服务端暂时看的不深,大体上可以理解为专门有一个TcpSocketReciever接收数据,由于数据在传输过程中是需要序列化的。因此接收后首先要进行decode,生成消息树。然后把消息放入BlockingQueue,有分析器不断的来队列拿消息树进行分析,分析后按照一定的规则把报表存储到数据库,把原始数据存储到本地文件中(默认是存储到本地)。

因此数据在服务端的流程大致可以理解为:

网络流-->decode反序列化-->BlockingQueue-->analyzer分析--->报表存储在DB
                                                    |---->原始数据存储在本地或hdfs

简单的Transaction例子

在Cat里面,消息大致可以分为几个类型:

  • Transaction 有可能出错、需要记录处理的时间的监控,比如SQL查询、URL访问等
  • Event 普通的监控,没有处理时间的要求,比如一次偶然的异常,一些基本的信息
  • Hearbeat 心跳检测,常常用于一些基本的指标监控,一般是一分钟一次
  • Metric 指标,比如有一个值,每次访问都要加一,就可以使用它

Transaction支持嵌套,即可以作为消息树的根节点,也可以作为叶子节点。但是Event、Heartbeat和Metric只能作为叶子节点。有了这种树形结构,就可以描述出下面这种调用链的结果了:
<iframe id="iframe_0.24694579557393959" style="border-width: initial; border-style: none; width: 901px; height: 287px;" src="data:text/html;charset=utf8,%3Cimg%20id=%22img%22%20src=%22http://unidal.org/cat/images/logviewAll02.png?_=6237874%22%20style=%22border:none;max-width:901px%22%3E%3Cscript%3Ewindow.onload%20=%20function%20()%20%7Bvar%20img%20=%20document.getElementById('img');%20window.parent.postMessage(%7BiframeId:'iframe_0.24694579557393959',width:img.width,height:img.height%7D,%20'http://www.cnblogs.com');%7D%3C/script%3E" frameborder="0" scrolling="no" width="320" height="240"></iframe>

Transaction和Event的使用很简单,比如:

 @RequestMapping("t")
    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!";
    }

这是一个最基本的Transaction的例子。

分布式调用链监控

在分布式环境中,应用是运行在独立的进程中的,有可能是不同的机器,或者不同的服务器进程。那么他们如果想要彼此联系在一起,形成一个调用链,就需要通过几个ID进行串联。这种串联的模式,基本上都是一样的。

举个例子,A系统在aaa()中调用了B系统的bbb()方法,如果我们在aaa方法中埋点记录上面例子中的信息,在bbb中也记录信息,但是这两个信息是彼此独立的。因此就需要使用一个全局的id,证明他们是一个调用链中的调用方法。除此之外,还需要一个标识谁在调用它的ID,以及一个标识它调用的方法的ID。

总结来说,每个Transaction需要三个ID:

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

其实ParentId和ChildId有点冗余,但是Cat里面还是都加上吧!

那么问题来了,如何传递这些ID呢?在Cat中需要你自己实现一个Context,因为Cat里面只提供了一个内部的接口:

public interface Context {
        String ROOT = "_catRootMessageId";
        String PARENT = "_catParentMessageId";
        String CHILD = "_catChildMessageId";

        void addProperty(String var1, String var2);

        String getProperty(String var1);
    }

我们需要自己实现这个接口,并存储相关的ID:

public class MyContext implements Cat.Context,Serializable{

    private static final long serialVersionUID = 7426007315111778513L;

    private Map<String,String> properties = new HashMap<String,String>();

    @Override
    public void addProperty(String s, String s1) {
        properties.put(s,s1);
    }

    @Override
    public String getProperty(String s) {
        return properties.get(s);
    }
}

由于这个Context需要跨进程网络传输,因此需要实现序列化接口。

在Cat中其实已经给我们实现了两个方法logRemoteCallClient以及logRemoteCallServer,可以简化处理逻辑,有兴趣可以看一下Cat中的逻辑实现:

//客户端需要创建一个Context,然后初始化三个ID
public static void logRemoteCallClient(Cat.Context ctx) {
        MessageTree tree = getManager().getThreadLocalMessageTree();
        String messageId = tree.getMessageId();//获取当前的MessageId
        if(messageId == null) {
            messageId = createMessageId();
            tree.setMessageId(messageId);
        }

        String childId = createMessageId();//创建子MessageId
        logEvent("RemoteCall", "", "0", childId);
        String root = tree.getRootMessageId();//获取全局唯一的MessageId
        if(root == null) {
            root = messageId;
        }

        ctx.addProperty("_catRootMessageId", root);
        ctx.addProperty("_catParentMessageId", messageId);//把自己的ID作为ParentId传给调用的方法
        ctx.addProperty("_catChildMessageId", childId);
    }
    
//服务端需要接受这个context,然后设置到自己的Transaction中
public static void logRemoteCallServer(Cat.Context ctx) {
        MessageTree tree = getManager().getThreadLocalMessageTree();
        String messageId = ctx.getProperty("_catChildMessageId");
        String rootId = ctx.getProperty("_catRootMessageId");
        String parentId = ctx.getProperty("_catParentMessageId");
        if(messageId != null) {
            tree.setMessageId(messageId);//把传过来的子ID作为自己的ID
        }

        if(parentId != null) {
            tree.setParentMessageId(parentId);//把传过来的parentId作为
        }

        if(rootId != null) {
            tree.setRootMessageId(rootId);//把传过来的RootId设置成自己的RootId
        }

    }

这样,结合前面的RMI调用,整个思路就清晰多了.

客户端调用者的埋点:

@RequestMapping("t2")
    public @ResponseBody String test2() {
        Transaction t = Cat.newTransaction("Call","test2");
        try{
            Cat.logEvent("Call.server","localhost");
            Cat.logEvent("Call.app","business");
            Cat.logEvent("Call.port","8888");

            MyContext ctx = new MyContext();
            Cat.logRemoteCallClient(ctx);

            IBusiness business = (IBusiness) Naming.lookup("rmi://localhost:8888/Business");
            business.echo("xingoo",ctx);
        }catch(Exception e){
            Cat.logError(e);
            t.setStatus(e);
        }finally {
            t.setStatus(Transaction.SUCCESS);
            t.complete();
        }
        return "cross!";
    }

远程被调用者的埋点:

interface IBusiness extends Remote{
    String echo(String message,MyContext ctx) throws RemoteException;
}
class BusinessImpl extends UnicastRemoteObject implements  IBusiness {
    public BusinessImpl() throws RemoteException {}
    @Override
    public String echo(String message,MyContext ctx) throws RemoteException {
        Transaction t = Cat.newTransaction("Service","echo");
        try{
            Cat.logEvent("Service.client","localhost");
            Cat.logEvent("Service.app","cat-client");
            Cat.logRemoteCallServer(ctx);
            System.out.println(message);
        }catch(Exception e){
            Cat.logError(e);
            t.setStatus(e);
        }finally {
            t.setStatus(Transaction.SUCCESS);
            t.complete();
        }
        return "hello,"+message;
    }
}
public class RpcServer {
    public static void main(String[] args) throws RemoteException, AlreadyBoundException, MalformedURLException {
        IBusiness business = new BusinessImpl();
        LocateRegistry.createRegistry(8888);
        Naming.bind("rmi://localhost:8888/Business",business);
        System.out.println("Hello, RMI Server!");
    }
}

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

分享到:
评论

相关推荐

    微服务调用链监控CAT架构和实践

    2. 调用链追踪:通过示例展示如何使用CAT追踪服务调用链,理解事务ID和父子事务的概念。 3. 指标监控与报警设置:学习如何配置和查看各项指标,以及如何设置告警规则。 4. 自定义监控与异常处理:介绍如何添加自定义...

    点评CAT服务cat.war

    总结来说,"点评CAT服务cat.war"主要涉及分布式系统监控、服务调用链路追踪、健康检查、业务指标监控等多个方面,是现代互联网企业不可或缺的运维工具。通过对`cat.war`及相关文件的部署和配置,我们可以实现对系统...

    cat源码研究文档

    3. **性能监控**:Cat提供了基于调用链的性能分析,通过追踪每个请求的完整生命周期,能够定位性能瓶颈。这部分源码主要集中在Profiler模块。 4. **故障排查**:当系统出现异常时,Cat的报警机制会立即发送通知,...

    CAT解析_实战.pdf

    CAT是一个分布式服务追踪系统,主要用于收集和处理服务调用信息,以便于开发者和运维人员了解服务调用的性能,以及服务调用之间的依赖关系。 4. 文件的某些部分内容提到了“CATServer总体架构”,“CATNetty网络...

    Cat 客户端埋点插件用户手册1

    在分布式服务调用中,Cat能够追踪dubbo的服务调用过程,记录调用耗时、异常等信息,有助于优化服务间的通信效率。 3.6 Http远程调用 对于HTTP请求,Cat提供了埋点功能,记录请求的URL、响应时间、状态码等,以便...

    大众点评CAT监控系统

    5. **分布式事务监控**:对于跨服务的复杂事务,CAT能够追踪事务的完整生命周期,确保数据的一致性。 6. **性能统计**:对系统资源如CPU、内存、磁盘使用情况进行监控,为系统优化提供依据。 7. **报警服务**:...

    cat性能监控demo资源

    - **服务调用链追踪**:记录每一次服务间的调用过程,帮助定位问题。 - **业务监控**:实时展示业务指标,如QPS(每秒查询率)、成功率等。 - **健康检查**:检测系统服务是否正常运行。 - **报警机制**:当异常...

    构建一个分布式系统的简单方案

    3. **SSH远程调用**:利用SSH实现对远程机器上程序和服务的安全调用,支持基于用户的细粒度权限控制。 通过这种方式,不仅解决了进程唯一标识的问题,还简化了远程通信的实现,为构建分布式系统提供了一种可行且...

    apache-skywalking-java-agent-8.13.0 apache-skywalking-apm-9.0.0

    Skywalking是分布式系统的应用程序性能监视工具,专为微服务,云原生架构和基于容器(Docker,K8S,Mesos)架构而设计,它是一款优秀的APM(Application Performance Management)工具,包括了分布式追踪,性能指标...

    时代凌宇skywalking链路监控1

    分布式链路追踪技术如 SkyWalking、Zipkin、Pinpoint 和 CAT(大众点评开源项目),则能解决这个问题,它们追踪并记录各服务间的调用关系,便于分析请求处理过程中的性能瓶颈和错误。 3. SkyWalking 特性与原理 - ...

    CAT实时应用监控平台 v3.0.0-源码.zip

    1. **数据采集**:CAT通过在应用代码中插入探针,实现对业务操作的追踪,收集如事务、消息、异常等关键指标。源码中会包含这些探针的实现逻辑,以及如何将收集到的数据封装成特定格式。 2. **数据传输**:CAT采用...

    cat分享相关

    Cat是Hadoop社区的一个子项目,它提供了一个分布式实时监控系统,主要服务于大规模分布式系统的性能监控和故障排查。Cat的核心功能包括服务发现、监控报警、日志聚合和业务追踪。 在Java环境中,Cat主要通过添加...

    CAT监控中心技术总结.doc

    CAT是一个分布式应用监控平台,它可以收集和分析各种应用指标,包括但不限于请求响应时间、调用链路、错误率、吞吐量等,提供丰富的图表展示和报警机制,使得运维人员可以及时发现和处理问题。 2. CAT能给我们带来...

    zipkin.zip

    分布式链路追溯:指在以数百几计的分布式服务系统中,能做到可以追踪每一次服务的调用性能,相互调用链路,以便于问题排查和性能分析。分布式链路跟踪框架主要包括了客户端、服务器和 UI。客户端集成到各个服务中,...

    Tech Salon 008 - 分布式监控系统实践.6cf354e0-39bc-11e6-bce6-b7dd80bb9649.

    在“Tech Salon 008 - 分布式监控系统实践”中,我们聚焦于一个名为CAT(Central Application Tracking)的工具,它是一个专门为Java应用设计的全栈监控解决方案。 CAT的主要功能包括: 1. **服务监控**:CAT能够...

    scala-opentracing:Cat和Http4s的跟踪库,专门为Datadog和Jaeger等Opentracing跟踪器量身定制

    Cat不仅支持基本的日志记录,还支持分布式追踪,可以与其他OpenTracing实现无缝集成。在scala-opentracing中,Cat被用作本地的追踪代理,负责收集和管理追踪信息。 **Http4s** 是一个Scala的HTTP服务器和客户端库,...

    中国式微服务架构模拟案例.docx

    3. **分布式调用链监控**:采用了大众点评的CAT,而非Spring Cloud Sleuth + Zipkin组合,CAT支持非侵入式的调用链追踪。 #### 六、总结 本案例通过整合Spring Cloud核心组件、国内领先技术及自定义开发的技术组件...

    京东微服务架构监控及监控原理介绍

    如文章中提到的以Cat、Zipkin为代表的开源追踪工具,虽然相比其他监控工具体积较大,需要额外的埋点或插件支持,但它们能够提供全量或采样的事务追踪,大大增强了解决问题的能力。 在构建微服务架构监控体系时,还...

    service-governance-1.zip

    CAT是阿里巴巴开源的一款实时监控系统,它能够帮助开发者监控分布式系统的性能和健康状况,特别是通过调用链追踪,可以清晰地了解服务之间的调用关系和性能瓶颈,对于优化服务治理有着极大的帮助。 其次,"angular-...

Global site tag (gtag.js) - Google Analytics