说起CAT,需要先简单介绍一下分布式服务链路监控。
随着微服务技术的普及,现在的系统体积变得越来愈庞大,功能变得越来越复杂。
一个简单的接口,如查询操作,其背后可能涉及到几十甚至上百的服务调用,不同的服务又由不同的团队开发维护。
如果没有一套好的调用链监控,当系统出现故障、服务之间依赖出现问题的时候,就会非常难以定位问题。
另一方面,还需要对这些数量庞大的服务进行各种指标的监控,如相应时间、请求量(QPS、PV)、错误率等,以确保整个系统的高效、健康运行。
所以,分布式服务链路监控对于微服务来说是不可或缺的,相应的,各种服务监系统也应运而生。
2002年,eBay,CAL。
2010年,Google发表论文 ,"Dapper, a Large-Scale Distributed System Tracing Infrastructure"
2011年,大众点评,CAT
Twitter,Zipkin
阿里巴巴,鹰眼
接下来进入本文的主题
CAT(Central Application Tracking)是原大众点评基于eBay的CAL改进而来的分布式服务链路监控平台。
CAT自2004年开源以来,在携程、陆金所、平安银行、拼多多、OPPO、猎聘网、找钢网等100多家公司、企业的生产环境中落地应用。以2016年的一组数据为例:
美团点评,2016年,3000+应用服务,7000+服务器,100+TB/天,单机峰值QPS 16w
携程,2016年,4500+应用服务,10000+服务器,140TB/天,单机峰值QPS 10w
目前,CAT(作为服务端项目基础组件,提供了 Java, C/C++, Node.js, Python, Go 等多语言客户端,已经在美团点评的基础架构中间件框架(MVC框架,RPC框架,数据库框架,缓存框架等,消息队列,配置系统等)深度集成,为美团点评各业务线提供系统丰富的性能指标、健康状况、实时告警等。
整体设计
监控整体要求就是快速发现故障、快速定位故障以及辅助进行程序性能优化。为了做到这些,CAT对监控系统的一些非功能做了如下的要求:
- 实时处理:信息的价值会随时间锐减,尤其是事故处理过程中。
- 全量数据:最开始的设计目标就是全量采集,全量的好处有很多。
- 高可用:所有应用都倒下了,需要监控还站着,并告诉工程师发生了什么,做到故障还原和问题定位。
- 故障容忍:CAT本身故障不应该影响业务正常运转,CAT挂了,应用不该受影响,只是监控能力暂时减弱。
- 高吞吐:要想还原真相,需要全方位地监控和度量,必须要有超强的处理吞吐能力。
- 可扩展:支持分布式、跨IDC部署,横向扩展的监控系统。
- 不保证可靠:允许消息丢失,这是一个很重要的trade-off,目前CAT服务端可以做到4个9的可靠性,可靠系统和不可靠性系统的设计差别非常大。
CAT从开发至今,一直秉承着简单的架构就是最好的架构原则,主要分为三个模块:CAT-client、CAT-consumer、CAT-home。
- Cat-client 提供给业务以及中间层埋点的底层SDK。
- Cat-consumer 用于实时分析从客户端提供的数据。
- Cat-home 作为用户给用户提供展示的控制端。
在实际开发和部署中,Cat-consumer和Cat-home是部署在一个JVM内部,每个CAT服务端都可以作为consumer也可以作为home,这样既能减少整个层级结构,也可以增加系统稳定性。
上图是CAT目前多机房的整体结构图:
- 路由中心是根据应用所在机房信息来决定客户端上报的CAT服务端地址
- 每个机房内部都有的独立的原始信息存储集群HDFS
- cat-home可以部署在一个机房也可以部署在多个机房,在做报表展示的时候,cat-home会从cat-consumer中进行跨机房的调用,将所有的数据合并展示给用户
- 实际过程中,cat-consumer、cat-home以及路由中心都是部署在一起,每个服务端节点都可以充当任何一个角色
客户端架构设计
CAT客户端是java,客户端在收集端数据方面使用ThreadLocal,是线程本地变量,也可以称之为线程本地存储。线程局部变量(ThreadLocal)其实的功用非常简单,就是为每一个使用该变量的线程都提供一个变量值的副本,是Java中一种较为特殊的线程绑定机制,是每一个线程都可以独立地改变自己的副本,而不会和其它线程的副本冲突。
在监控场景下,为用户提供服务都是web容器,web容器比如Tomcat或者Jetty,后端的rpc服务端比如dubbo或者点评自研的服务框架pigeon,也都是基于线程池来实现的。业务方在处理业务逻辑时基本都是在一个线程内部调用后端服务,数据库,缓存等,将这些数据拿回来在进行业务逻辑逻辑封装,最后将结果展示给用户。所以将所有的监控请求作为一个监控上下文存入于线程变量就非常合适。如上图业务执行业务逻辑的时候,就会把此次请求对应的监控存放于线程上下文中,存于上下文的其实是一个监控树的结构。在最后业务线程执行结束时,将监控对象存入一个异步内存队列中,CAT有个消费线程将队列内的数据异步发送到CAT服务端。
服务端架构设计
服务端单机cat-consumer的整体架构如下:如上图,CAT服务端在整个实时处理中,基本上实现了全异步化处理。
- 消息接收是基于Netty的NIO实现
- 消息接收到服务端就存放内存队列,然后程序开启一个线程会消费这个消息做消息分发
- 每个消息都会有一批线程并发消费各自队列的数据,以做到消息处理的隔离
- 消息存储是先存入本地磁盘,然后异步上传到hdfs文件,这也避免了强依赖hdfs
当某个报表处理器处理来不及时候,比如Transaction报表处理比较慢,可以通过配置支持开启多个Transaction处理线程,并发消费消息。
监控模型
CAT主要支持以下四种监控模型:
- Transaction 适合记录跨越系统边界的程序访问行为,比如远程调用,数据库调用,也适合执行时间较长的业务逻辑监控,Transaction用来记录一段代码的执行时间和次数
- Event 用来记录一件事发生的次数,比如记录系统异常,它和transaction相比缺少了时间的统计,开销比transaction要小
- Heartbeat 表示程序内定期产生的统计信息, 如CPU利用率, 内存利用率, 连接池状态, 系统负载等
- Metric 用于记录业务指标、指标可能包含对一个指标记录次数、记录平均值、记录总和,业务指标最低统计粒度为1分钟
CAT报表
Transaction报表 :
一段代码的运行情况,如运行次数、QPS、错误次数、失败率、响应时间统计(平均影响时间、Tp分位值)等等。Event报表 :
监控一段代码运行次数,例如记录程序中一个事件记录了多少次,错误了多少次。
Problem报表 :
记录整个项目在运行过程中出现的问题,包括一些异常、错误、访问较长的行为。Problem报表是由logview存在的特征整合而成,方便用户定位问题。
- Long-url,表示Transaction打点URL的慢请
- Long-sql,表示Transaction打点SQL的慢请求
- Long-service,表示Transaction打点Service或者PigeonService的慢请求
- Long-cache,表示Transaction打点Cache.开头的慢请求
- Long-call,表示Transaction打点Call或者PigeonCall的慢请求
Heartbeat报表 (JVM相关的状态信息)
- LoadAverage
- FreePhysicalMemory
- FreeSwapSpaceSize
- PS ScavengeCount
- PS ScavengeTime
- PS MarkSweepCount
- PS MarkSweepTime
Storage报表 (数据库相关)
Cache报表 (缓存相关)
参考链接:
https://tech.meituan.com/2018/11/01/cat-in-depth-java-application-monitoring.html
https://github.com/dianping/cat