简介
淘宝现在是一个由很多个应用集群组成的非常复杂的分布式系统。这些应用里面主要有处理用户请求的前端系统和有提供服务的后端系统等。这些应用之间一般有RPC调用和异步消息通讯两种手段,RPC 调用会产生一层调一层的嵌套,一个消息发布出来更会被多个应用消费,另外,应用还会访问分库分表的数据库、缓存、存储等后端,以及调用其他外部系统如支付、物流、机彩票等。
请试想一下,现在淘宝一个买家点击下单按钮所产生的网络请求到达淘宝服务器之后,就会触发淘宝内网数百次的网络调用。这些调用中有哪些出问题会影响这次交易,有哪些步骤会拖慢整个处理流程,双十一的交易高峰需要给应用集群分配多少台机器,这些都是需要考虑的。但是调用环境的复杂度,已经很难用人力去做准确的分析和评估了,这时候 EagleEye 就派上了用场。
EagleEye (鹰眼)通过收集和分析在不同的网络调用中间件上的日志埋点,可以得到同一次请求上的各个系统的调用链关系,有助于梳理应用的请求入口与服务的调用来源、依赖关系,同时,也对分析系统调用瓶颈、估算链路容量、快速定位异常有很大帮助。另外,业务方也可以在调用链上添加自己的业务埋点,使网络调用和实际业务内容得到关联。
目前互联网公司类似的产品有:Google Dapper、Twitter Zipkin。
EagleEye 特色功能
调用链的异常跟踪
下面这幅图,展示了在 mtop 上抛的异常顺便打印的 TraceId,可以在 EagleEye 直接定位到是第 4 层 TAIR 网络请求失败导致的问题。PS:日常环境也同样支持调用链跟踪。
应用关键路径(强弱依赖分析 / 瓶颈分析 / 易故障点)
EagleEye 为应用自动产生的关键路径分析报表,除了可以看到 QPS、秒级峰值 QPS之外,还可以看到强弱依赖关系、分布式调用的瓶颈点、经常出错的依赖等。
突发流量来源跟踪
HSF 服务、DB 有突发的调用高峰,来自什么地方??妈妈再也不用担心我束手无策了,EagleEye 直接给你答案。
入口实时大盘
利用 EagleEye 独有的入口大盘,可以清晰看到某个前端 URL,它直接依赖、间接依赖的所有后端服务的调用情况。某个前端网页变白板时,从 EagleEye 入口大盘也许能看出点端倪。
应用实时依赖情况
应用依赖情况尽收眼底。
服务实时调用大盘
服务再多,心里也有数。
EagleEye 的核心
TraceId
在复杂的分布式系统环境下,EagleEye是一个有广泛用途的调用分析和问题排查工具。与一般的调用信息埋点日志相比,EagleEye埋点的一个显著的不同点在于它的每条日志都有与每次请求关联的上下文ID,我们称为TraceId。通过TraceId,后期的日志处理时可以把一次前端请求在不同服务器记录的调用日志关联起来,重新组合成当时这个请求的调用链。因此,EagleEye不仅可以分析到应用之间的直接调用关系,还可以得到他们的间接调用关系、以及上下游的业务处理信息;对于调用链的底层系统,可以追溯到它的最上层请求来源以及中间经过的所有节点;对于调用链的上层入口,可以收集到它的整棵调用树,从而定位下游系统的处理瓶颈,当下游某个应用有异常发生时,能迅速定位到问题发生的位置。
如上图所示,应用A是接受到来自用户浏览器的Web请求的前端服务器,它是一条调用链的开始端,在TBSession和EagleEyeFilter中都做了EagleEye上下文埋点。请求收到后它会先调用EagleEye StartTrace生成TraceId并放置在当前线程的ThreadLocal,日志埋点请求信息(如URL、SessionId、UserId等)。在请求处理完毕提交相应时,再调用EndTrace清理线程中的EagleEye信息。
在应用A调用应用B、C的HSF服务,或者发送Notify消息时,TraceId被包含在EagleEye上下文中,随网络请求到达应用B、C、D、E之中,并放置在线程ThreadLocal内,因此后续调用到的这些系统都会有EagleEye这次请求的上下文。这些系统再发起网络请求时,也类似的携带了上下文信息的。
RpcId
为了区别同一个调用链下多个网络调用的顺序和嵌套层次,EagleEye还需要传输和记录RpcId。
RpcId用0.X1.X2.X3.....Xi来表示,Xi都是非负整数,根节点的RpcId固定从0开始,第一层网络调用的RpcId是0.X1,第二层的则为0.X1.X2,依次类推。例如,从根节点发出的调用的RpcId是0.1、0.2、0.3,RpcId是0.1的节点发出的RpcId则为0.1.1、0.1.2、0.1.3。如下图所示。
通过RpcId,可以准确的还原出调用链上每次调用的层次关系和兄弟调用之间的先后顺序。
例如上图应用 G 的两次调用0.2.1.1和0.1.2.1,可以看出对 DB 的访问0.2.1.1源于 C 到 G 的调用0.2.1,对 Tair 的访问0.1.2.1源于B 到 G 的调用0.1.2。
很多调用场景会比上面说的完全同步的调用更为复杂,比如会遇到异步、单向、广播、并发、批处理等等,这时候需要妥善处理好ThreadLocal上的调用上下文,避免调用上下文混乱和无法正确释放。另外,采用多级序号的RpcId设计方案会比单级序号递增更容易准确还原当时的调用情况。
中间件埋点
EagleEye 需要在网络调用中间件中埋点,实现TraceId/RpcId等上下文信息的生成、传输,以及调用日志的记录。这些信息记录在ThreadLocal,对应用是透明的,应用也不需要修改方法API来回传递EagleEye的上下文对象。
目前EagleEye在淘宝最常见的中间件HSF、Notify、TDDL、Tair、TFS、tbsession、tengine、metaQ、search都已经加上了EagleEye的埋点,基本涵盖了服务调用、消息通信、数据库存取、缓存数据存取、前端接入等各种场景。越来越多的网络调用中间件会逐步接入到 EagleEye 体系当中。
透明数据传输
EagleEye 本身具备透明传输 TraceId、RpcId 的能力,这个能力通用可以用来传输业务的一些数据。例如,如果希望追加自己的业务信息到调用链上,应用可以通过 EagleEye 提供的 API 进行链路分组、链路打标等,EagleEye 会把这些信息一层层的透明往后端系统传递,而且信息也同样记录在每条调用日志里面。
EagleEye 数据透传的功能,在全链路压测、子帐号信息传递、二套环境项目隔离、强弱依赖检测、敏感操作的前端来源跟踪等场景都起到了不可缺少的重要作用。
整体处理架构
下面是EagleEye从日志生成到抓取、存储、分析、展现的多个系统间交互过程。
首先,EagleEye的埋点日志会输出到统一的日志目录下,与业务无关的RPC日志会写文件eagleeye.log,业务埋点日志会写文件biz-eagleeye.log。
随后,tlog服务器会通过每台应用机器上的哈勃agent实时的不断抓取EagleEye日志,按照日志类型不同,将会有不同的处理方式:
- 全量原始日志都会直接存到云梯HDFS中;
- 带有实时标记的EagleEye原始日志,放入HBase中存储;
- 业务日志有一部分会被直接处理,存储到HBase,有一部分会作为消息发送出去,由感兴趣的业务系统订阅处理。
- 全量日志存储到 HDFS 之后,EagleEye服务器会每小时创建 MapReduce 任务,在云梯上完成对全量日志的调用链合并、分析以及统计,合并好的调用链依旧保存在HDFS*,分析和统计的结果也输出到HDFS,EagleEye服务器在任务执行完毕之后,会将结果拉取到本地建立索引,并保存到HBase。在保存完毕后,就可以在EagleEye的数据展现页面看到分析结果了。
- 另外,EagleEye 还做了调用的实时统计,有分钟级别的实时链路大盘、应用大盘、服务大盘和入口大盘,帮助快速定位故障问题所在。