天天看点

分布式调用跟踪与监控实战

分布式调用系统的现状

当前,随着互联网架构的扩张,分布式系统变得日趋复杂,越来越多的组件开始走向分布式化,如微服务、消息收发、分布式数据库、分布式缓存、分布式对象存储、跨域调用,这些组件共同构成了繁杂的分布式网络。

分布式调用跟踪与监控实战

如上图右侧所示,当应用A发出某个请求时,其背后可能有数十个甚至更多的服务被调用,可谓是“牵一发而动全身”。

如果将分布式系统比作高速公路网,每个前端的请求就相当于高速上行驶的车辆,而处理请求的应用就是高速上的收费站,在收费站上将车辆通行信息记录成日志,包括时间、车牌、站点、公路、价格等,如果将所有收费站上的日志整合在一起,便可以通过唯一的车牌号确定该车的完整通行记录;分布式调用系统跟踪和监控就是类比这种思想,对每一次请求进行跟踪,进而明确每个请求所经过的应用、耗时等信息。

<b>阿里巴巴的分布式调用跟踪实现——鹰眼</b>

分布式调用跟踪与监控实战

阿里巴巴中分布式调用跟踪是采用鹰眼(EagleEye)系统来实现的,鹰眼是基于日志的分布式调用跟踪系统,其理念脱胎于Google Dapper论文,其关键核心在于调用链,为每个请求生成全局唯一的ID(Traceld),通过它将不同系统的“孤立的”调用信息关联在一起,还原出更多有价值的数据。

分布式调用跟踪与监控实战

上图是一条来自生成环境的调用链,在应用名列可以看到请求中间过程所经过的一系列应用,可以看到最先经过Buy应用,后续调用delivery、tee、inventoryplatform等,形成调用树(树上的缩进表示嵌套关系),从调用树上很容易看到前端请求的完整处理过程。

另外值得注意的一点,上图是由白色背景和蓝色背景组成。其中蓝色背景表示调用链经过消息之后,变成了异步的消息通道,其后续处理过程也都是异步处理过程;白色背景处表示同步过程。一般而言,对于前端的用户等待的时间是不包含蓝色背景内的耗时,也就是只包含了同步处理的时间。

在上图所示的页面中也清晰地展示了每块应用处理请求得具体耗时,非常直观地进行定位;此外,状态信息也是值得关注的一点,如上图所示,如果在调用过程中发生错误,就会出现异常(图中红色区域所标注),通过点击状态码,用户可以查看错误的具体信息。

鹰眼于2013年在阿里巴巴内部上线,目前支撑阿里集团泛电商、高德、优酷等业务,技术层面覆盖前端网关接入层、远端服务调用框架(RPC)、消息队列、数据库、分布式缓存、自定义组件(如支付、搜索SDK、本地方法埋点等);2016年在阿里云的中间件云产品EDAS发布,对外提供服务;此外,鹰眼也支持私有云输出。

使用场景

下面来具体看一下调用链的具体使用场景。

<b>定位异常、耗时问题</b>

分布式调用跟踪与监控实战

可以在业务异常日志的错误信息中找到Traceld(如图中的TraceId=ac18287913742691251746923),之后在鹰眼系统中只需要输入Traceld,就可以看到调用链中具体的情况,在调用链上更加直观地定位到问题(如上图所示),层层排查后确定问题的所在。

<b>带调用链下钻的监控报表</b>

分布式调用跟踪与监控实战

对于分布式调用跟踪系统而言,它并不仅仅提供了调用链这一功能,因为它对所有中间件的调用做埋点,所以中间件上的所有情况都可以监控的到。因此,在形成调用链的过程中也会形成一份详细的调用监控报表,它与其他监控的不同之处在于:该监控报表是带有上下钻取功能的报表。因为调用链是详细的底层统计,对上可以形成的报表维度是非常丰富的,在上图所示的调用报表里,不仅可以看到服务的情况,还可以下钻到它所调用服务的情况;另外从监控报表上还可以进行调用链的下钻,查看清晰的调用链信息。

<b>链路分析</b>

链路与调用链不同,链路是一个统计学的概念,而调用链是单体调用的过程。分析链路的价值主要体现在以下几点:

(1)拓扑形态分析:分析来源、去向,识别不合理来源;

(2)依赖树立:识别易故障点/性能瓶颈、强依赖等问题;

(3)容量估算:根据链路调用比例、峰值QPS评估容量;

(4)异常个体识别:寻找集群中与其他个体有差异的实例。

下面来具体分析这四点。

<b>拓扑形态分析:分析来源、去向,识别不合理来源</b>

分布式调用跟踪与监控实战

上图是全局调用拓扑图,可以明显的看到不同的应用之间存在复杂的调用关系,也可以查看某个应用和其他应用之间的调用关系以及调用的频次;图中红点表示在调用过程中出现错误。

通过该拓扑图,架构师可以清楚地观察到系统上的调用情况,此外,点击全局调用拓扑图上的某个节点,可以下钻到下图所示的单应用链路拓扑图。

分布式调用跟踪与监控实战

在以某应用为中心的单应用链路拓扑图,可以查看该应用在调用链上下游的应用之间的具体调用关系。

<b>依赖梳理和容量估算</b>

链路分析除了进行拓扑形态分析之外,还能进行依赖梳理:识别易故障点、性能瓶颈、强依赖等问题;也可以根据链路调用比例、峰值QPS 评估容量。

分布式调用跟踪与监控实战

上图是一份单链路报表,单链路报表是指同一HTTP入口的调用链叠加形成、包含所有依赖情况的调用关系。上图左侧模糊部分是一棵调用树,它表现了应用之间的依赖关系,与调用链不同的是,这种依赖关系是统计学意义上的依赖,因此在该报表上包含了QPS和统计QPS统计类型的数据。在进行容量预估时,可以很容易分析上游应用对下游造成的压力。

在该报表上,还可以进行依赖梳理方面的工作,根据出错率确定易故障点;此外,那些存在强依赖、错误阻塞的地方都是潜在故障点;最后,还可以根据耗时比例进行相关的性能优化。

<b>异常个体识别:寻找集群中与其他个体有差异的实例</b>

异常个体识别是链路分析的另一大作用,可以很容易地识别集群中与其他个体存在差异的实例。

分布式调用跟踪与监控实战

在集群中,由于网络或机器配置等原因导致不同的计算机处理能力有差别,因此可能存在某些机器空闲而其他机器繁忙的现象,正如上图的热点图显示,集群中会存在负载不均匀的情况,通过调用链可以非常容易地识别集群中存在问题的机器;此外,还可以通过离散点、波形图等方式发现异常的个体。

分析出异常个体只是链路分析的第一步,分析出异常个体之后,能够自动提醒用户或者是自动处理异常情况才是后续的关键问题,而不是人工去检查每个报表中是否存在异常。

实现原理

在分析调链实现的原理之前,首先需要明确几个关键概念:

(1)全局唯一ID:Traceld。Traceid是与每次请求相对应,保证全局唯一;用于将调用链的各个调用重新关联起来。Traceld的实现有以下三种方案:

方案一:由中心节点统一分配。 方案二:在本地直接生成,无业务语言(UUID)。 方案三:在本地直接生成,但是附带业务语义。

方案一对中心节点过于依赖,存在性能问题;方案二不包含业务语言。因此,综合方案一、二,我们最后选择了方案三。

分布式调用跟踪与监控实战

图中给出了目前使用的Traceid的组成部分,包括IPv4、毫秒时间、顺序数、标志位、进程PID五部分。

 (2)调用链内部的唯一ID:RpcId(也叫SpanId)。它用于还原调用顺序和调用间的嵌套关系,需要考虑的调用关系包括同步、并发、异步、一对多。那么用什么方式实现RpcId 适合表示上述关系呢?

分布式调用跟踪与监控实战

阿里内部实现RpcId的方案(方案二)是多级序号的方案:生成一个RpcId的同时,也会包含所有上级的RpcId,具体示意图如上图所示。从上图可以形象地看到RpcId的概念,例如从0.3.1.1,可以得知它上一级序号为0.3.1,再由0.3.1得知其上一级序号为0.3,以及上上一级序号0;此外,多级序号的实现方式相对于单级序号方式在成本方面也得到降低。

另外,在整个调用链里还涉及其他一些关键概念:

(1)调用相关属性Tags:它记录仅和本次调用相关的信息,是一个K-V 集合。常见的Tags一般包括调用时间、调用耗时、调用类型、服务名、操作名、响应状态码、对端服务器地址等信息。

(2)调用透传信息UserData(也叫Baggage):它记录同一条调用链上下游共享的信息,也是一个K-V 集合。常见的UserData包括通用数据、特殊指令、调用路由控制,影响部分中间件的路由走向。

(3)调用上下文RpcContext(也叫SpanContext):它由TraceId、RpcId、Tags、UserData 共同组成,在进程内部时,上下文保存于ThreadLocal,对业务透明;在网络通讯时,上下文会和实际内容一起被传输到对端。

整体架构

下面来看一下分布式调用跟踪的整体架构。

分布式调用跟踪与监控实战

如上图所示,在架构的最上端是应用集群,每台机器中都有一个带鹰眼埋点的中间件,该中间件负责向日志文件中写入数据,每台机器上的数据收集agent从日志文件读取数据,实现实时收集日志;在鹰眼系统中通过实时处理集群对实时日志进行计算分析,得到两种类型的数据,分别是统计类型的报表(存放在HBase中)和调用链调用明细详情(存放在HiStore中);另外,涉及到离线数据分析的数据使用ODPS离线分析集群进行计算,主要是一些模型建设方面的分析。

架构中最下侧室鹰眼控制台,通过控制台可以查看实时处理集群后的结果;此外,控制台还负责实时处理集群和埋点相关的配置推送。

<b>整体处理流程</b>

数据整体处理流程抽象为埋点、采集、分析、存储四部分,下面来具体分下每个部分。

<b>埋点客户端</b>

埋点客户端首先需要注意减少对业务线程的影响,降低资源消耗;其次由于每个网络请求至少1次调用记录,QPS越高则数据产生越快,进而导致成本很难控制;另外,业务方是希望埋点对业务是无侵入的,部署在任何位置均可,进而导致运维环境相对复杂。

分布式调用跟踪与监控实战

为了解决上述挑战,在阿里内部我们选用了完全自行实现的基于日志的输出方案,在提升输出性能方面:采用异步无锁队列写日志(定制的Disruptor);减少输出内容,长字符串编码;日志输出缓存,限制IO次数,每秒刷新;高效多进程并发写文件。

在运维层面,也进行了很多优化:日志文件按大小滚动,自动清理;统一字符编码,统一时区;全局配置推送管控;异常状态自动降级。

因为对业务是无侵入埋点,所以在内部中间件直接进行埋点植入;对于非内部中间件,如开源组件,第一种方式是提供了埋点的组件包或通过扩展的方式增加埋点;第二种方法是使用AOP一类技术,在运行期对目标埋点位置做字节码增强;第三种方式是适配已有的链路埋点,如OpenTraceing或Zipkin等。

<b>调用链采样</b>

因为QPS越高,需要生成的调用日志也就越高。因此,为了降低整体的输出数据量,引入例如采样的概念,根据TraceId中的顺序数进行采样,提供了多种采样策略搭配:

• 100% 采样:它会对业务带来影响,因此最好内置支持客户端自动降级;

• 固定阈值采样:全局或租户内统一控制;

• 限速采样:在入口处按固定频率采样若干条调用链;

• 异常优先采样:调用出错时优先采样;

• 个性化采样:按用户ID、入口IP、应用、调用链入口、业务标识等配置开启采样。

通过上面这五种采样策略的搭配使用,可以灵活地控制调用链上数据的输出,确保数据量不会过大。

<b>数据采集</b>

分布式调用跟踪与监控实战

在数据采集方面主要面临内部和云上两个挑战:

•内部:在阿里内部,其实是一个比较可控的环境,但是因为数据量太大,每日百TB级别的规模,面临着节约成本的问题;

•云上:尽管数据量相对较小,由于网络环境比较复杂以及各种限制,数据采集依旧困难。

针对内部的挑战,我们的解决方案是在埋点时输出日志到本地,通过日志Agent读取日志,然后再通过实时计算的处理层主动拉取日志再进行处理。该方案直接复用应用机器存储日志,并且采用拉模式防止流量冲击过大。

针对云上的挑战,我们的解决方案利用消息队列的方式,埋点层主动发送消息,消息队列对消息进行存储,数据处理层从消息队列上订阅消息。这种方案可做到数据不丢,且主动推送可以提高实时性,环境适应性强;但这种解决方案的成本是比较高的。

<b>数据分析</b>

分布式调用跟踪与监控实战

数据采集的下一阶段是数据分析。数据分析主要分为调用级分析和链路级分析:

•调用级分析是指对单次调用数据,可以马上进行分析,按照指定的统计维度进行实时聚合即可,实时性为秒级。对于上文提到的采样情况下,为了保证数据统计的准确性,埋点层需要另外输出一份统计日志。

•链路级分析是调用链中最为关键的分析,它一般采用离线计算方案,将统一个TraceId的调用链先汇总在同一个Reduce任务重,调用链重组后再进行分析。类似于强弱依赖分析、调用频繁度分析、耗时瓶颈分析都是属于链路级分析。链路级分析的难点在于数据残缺补全,维度归一化和统计维度爆炸等问题。

<b>数据存储</b>

分布式调用跟踪与监控实战

在数据存储时,不同的数据类型存储的方式存在差异:

对于时间序列数据,我们是基于HBase存储,是OpenTSDB的修改后方案,解决聚合数据重复提交以及统计维度爆炸等问题。

对于调用链数据存储,分为三个阶段:

(1)阶段一:使用HBase存储,TraceId做rowkey;

(2)阶段二:使用Hadoop/ODPS 存储,按TraceId 的时间戳和哈希值进行分片,分片内按TraceId 排序;针对调用中记录的每个列进行针对性压缩,节省存储;

(3)阶段三:使用分库分表的HiStore 存储(将在DRDS 铂金版推出), 按TraceId 的时间戳和哈希值进行分库分表;HiStore 支持列式高压缩比存储,兼容MySQL 生态,非常适合写多读少的场景。

最佳实践

调用链作为排查问题的核心,通过其可以将各类数据关联在一起,提高问题排查能力。下面来看一下调用链的最佳实践——全息排查。

<b>全息排查</b>

分布式调用跟踪与监控实战

在实际问题排查中经常会遇到上图所示的问题,这些问题都具有明确的业务含义,这些问题尽管看上去和调用链并无关系,但可以用调用链得到很好的解决。如上图右侧所示,A-E五个节点在调用链上承载的调用关系实际上都是一些具体的业务,例如节点A处理HTTP请求表示卖家abc点击下单;在调用B时其实在计算卖家xyz在该路线的运费等等。在排查问题时,最有价值的切入点在于先从业务问题出发,再进一步在调用链中确认问题所在之处。

<b></b>

分布式调用跟踪与监控实战
分布式调用跟踪与监控实战

我们可以根据业务时间ID反查调用链,从而顺藤摸瓜找到更多的上下游业务信息。例如一个交易订单(2135897412389123)发现存在问题,我们可以根据订单号查到与之绑定的TraceId,根据TraceId不仅可以查看系统调用的事件,还可以看到与业务相关的事件,如用户下单、当前库存情况等,也就是说根据交易ID可以在调用链上查看交易、商品库存以及支付等信息,大大提升错误排查速度。

分布式调用跟踪与监控实战

回到刚才提到的三个问题:要分析由哪笔订单操作引起的调用异常其实是TraceId到OrderId的一次关联;要分析异常订单是否由卖家对所在商品的运费模板的某些异常操作导致其实是根据OrderId关联ItemId再关联TemplateId,最后关联到TraceId;对于第三个问题,通常是由UserId关联到TraceId再关联到MyBizld。

根据这些问题及其解决方案可以看到,全息排查的关键在于:业务时间id与TraceId/RpcId的双向绑定。

常见的双向绑定有三种实现方式:

(1)在调用链的Tags 或UserData 中放入业务事件id,从而建立调用链到业务事件id 的关联;

(2)打通TraceId 到数据库的数据变更的关联,从而建立调用链到每次数据变更的关联;

(3)在业务日志中记录TraceId、业务事件id 等信息,从而建立调用链与业务事件日志的关联。

目前,基于阿里云ARMS集成了上述三种双向绑定实现方式,用户可以在产品上轻松配置搞定。

<b>全息排查全景图</b>

分布式调用跟踪与监控实战

上图是阿里巴巴内部的全息排查全景图。该图的核心部分是鹰眼最初覆盖的的后端系统,包括服务、消息和缓存;在前端层面涉及前端用户访问日志,具有关联TraceId的能力;在移动端也具有关联TraceId的能力;通过对TraceId进行关联,可以打通用户访问日志;在数据库层面,通过SQL语句将TraceId传到数据库的binlog中,在数据复制和数据分发时可以非常容易获取到每次数据变更的记录与TraceId的关联;另外,业务通过自身的业务日志和异常堆栈也可以打印TraceId;这样一来,业务层、移动端、前端、数据层所有的组件都与TraceId进行了关联,再关联上业务中的订单号、用户号、商品号、物流单号、交易单号,最后形成一个非常强大的生态——从一个调用链可以看到上下游相关的订单、用户的详细信息,同时可以根据订单查到与该订单相关的业务ID,再根据业务ID扩展到与其相关的更多ID,甚至是TraceId,最后形成TraceId--&gt;业务ID--&gt;新的TraceId的网状结构,将排查问题转化为从网状结构中寻找需要的整段信息。

<b>通过EDAS+ARMS打造的立体化监控体系</b>

分布式调用跟踪与监控实战

目前,通过阿里云提供的EDAS结合ARMS可以打造立体化监控体系,其中EDAS用于应用管控层面,用于控制链路和应用;而ARMS更关注业务运营层面,如电商交易、车联网、零售;实际上,监控需要全方位关注业务、链路、应用、系统,通过ARMS与EDAS相互补全,形成了立体化监控体系。

继续阅读