小时光茶社
传说中天机阁里有一台掌控世间一切的机器,万物运行由此产生。本文的“天机阁”是一个基于链路跟踪的监控系统,后台开发人员能够通过“天机阁”洞察“天机”,快速解决问题。
摘要
为了支撑日益增长的庞大业务量,业界大量使用微服务架构。服务按照不同的维度进行拆分,互联网应用构建在不同的软件模块集上,这些软件模块可能是由不同的团队开发、可能使用不同的编程语言来实现、可能布在了几千台服务器,横跨多个不同的数据中心,分布式系统变得日趋复杂。
如何快速进行故障定位?如何准确进行容量评估?如何动态展示服务的链路?如何进行系统性能优化?这是分布式系统给后台开发同学带来的四大挑战。业界都是通过链路跟踪系统来解决以上问题,然而腾讯在链路跟踪方面比较欠缺。为了填补此空白,“天机阁”系统横空出世。
“天机阁”通过采集、存储、分析分布式系统中的trace数据、指标数据和日志数据,完成全链路跟踪,从而解决上述问题。目前天机阁接入了1200+个服务,trace数据上报峰值500万/分钟,指标数据上报峰值1.3亿/分钟,日志数据每天30T。如此海量的数据天机阁是怎样处理的?天机阁的链路跟踪是怎样实现的?又是怎样做到低侵入,低开销的?这些问题本文将一一为您揭秘。
背景
如摘要所述,企鹅电竞也采用微服务架构。图1是企鹅电竞首页接口的依赖关系图,这张图大家可能看不清楚,看不清楚才是正常的,因为系统依赖的服务太多了。这种情况下一个请求涉及几十个服务,若其中某个关键服务出现了失败,只知道有异常,但具体的异常在哪个服务引起的就需要进入每一个服务里面看日志,这样的处理效率是非常低的。
微服务的好处不用多说,然而微服务也是一把双刃剑,其坏处就是系统太复杂,后台开发者面临着以下四大问题。
1、故障定位难:一次请求往往需要涉及到多个服务,这些服务很可能是由多个团队负责的。一旦出问题,只知道有异常,但具体的异常在哪个服务引起的就需要进入每一个服务里面看日志,这样的处理效率是非常低的。最坏的情况可能要拉上多个团队一起定位。
2、容量评估难:企鹅电竞每个月都就有好几场推广活动。活动形式还经常变化,导致流量入口经常不同。企鹅电竞有500多个模块,不同入口的流量导致各模块的qps增量是不同的,容量评估是一件难事。接入天机阁之前,企鹅电竞的每次大型上量活动都需要2个开发同学花一整天的时间来做容量评估,事倍功半。
3、链路梳理难:一个新人加入后台团队,他在这个微服务体系中接手一个模块,根本不知道自己身在何处,不知道自己的系统被谁依赖了,也不知道自己的系统下游依赖哪些服务,需要看文档,一行行根据代码来分析,费时费力。
4、性能分析难:一个服务依赖于后台多个服务, 如果某接口的耗时突然增加,开发得从自己开始,逐步分析各依赖接口的耗时情况。
业界都是用分布式链路跟踪系统来解决上述问题。Dapper是谷歌生产环境下的分布式跟踪系统,算得上各大链路跟踪系统的鼻祖。2010年谷歌发表了Dapper的论文, 之后各大互联网公司纷纷参照dapper的思想推出各自的链路跟踪系统。包括Twitter的Zipkin,韩国人的pinpoint,Apache的HTrace,阿里的鹰眼Tracing、京东的Hydra、新浪的Watchman等。
腾讯在链路跟踪这块比较薄弱,需要尽快填补这个空白。以上几款链路跟踪系统都各自满足了链路追踪的功能,但落实到我们自己的生产环境中时,这些Trace系统存在诸多问题。 谷歌“dapper”和阿里“鹰眼”并不开源。Pinpoint和zipkin已经开源,然而pinpoint通过字节码注入的方式实现调用拦截和数据收集,仅能用于java服务器,Zipkin没有C++的版本,并且功能不够用。 最终我们选择用zipkin的协议,参照阿里鹰眼的架构自建一套腾讯内通用的链路跟踪系统----天机阁。
天机阁介绍
天机阁是一个以分布式链路跟踪为核心的监控系统。它通过采集、存储、分析分布式系统中的调用事件数据,再结合压测数据和TNM2数据,实现故障诊断、容量评估以及系统梳理等多种功能,大大降低开发人员的运维挑战, 见图2。数据采集架构图见图11。
注:“调用事件数据”包括“trace数据”、“指标数据”、“日志数据”三种。
trace数据——指链路跟踪的span数据,主要用于链路跟踪、还原、绘制拓扑图。
指标数据——指rpc的模调数据,包括分钟级别的rpc请求量、成功率、延时分布、错误码分布等等。
日志数据——业务日志。
1.故障定位:天机阁利用跟踪数据,可以还原调用链,再结合日志数据,可以快速实现故障定位。例如:用户投诉他的视频点赞数不对,开发同学可以根据用户qq号码找到投诉用户的请求trace。 查看trace详情就能很清楚的看到该请求在获取点赞结果的时候超时了,开发同学可以迅速完成定位并处理。Ui界面见图3。
图3:trace跟踪ui界面
2.链路梳理:有了跟踪数据,画出业务拓扑图就是水到渠成,图4是企鹅电竞某服务的拓扑图。
图4: rpc调用生成树
3.容量评估:天机阁的链路跟踪系统,能拿到各服务的调用拓扑图。 指标数据统计模块能拿到rpc指标数据,tnm2系统能获得服务的部署信息,根据这些信息。 压测系统能压测出各svr的性瓶颈。根据以上数据,天机阁能精确的评估出各服务的部署是否合理。 若遇到大型活动,开发者只需提供入口接口的qps增量, 天机阁就能预估出后续各依赖服务的qps增量,并给出推荐扩容数据。
图5:容量评估结果
4.性能分析:天机阁的压测系统能压测出单服务的性能数据,结合链路跟踪以及指标统计数据,能很好的分析出整个系统的性能瓶颈,找出优化方向。
5.其他功能:除以上4个功能外,天机阁还有实时告警,过载保护,名字服务,指标数据查询等多个实用功能,欢迎大家到天机阁系统体验。体验地址:http://tjg.oa.com (天机阁担心数据被意外修改或者泄漏,权限没有全员开放,感兴趣的同学可以找alexzeng申请权限)。
天机阁包含链路跟踪、压测系统、容量管理系统、名字服务四大系统,总体架构见图6。
链路跟踪系统:链路跟踪是天机阁的核心, 它负责采集、存储和分析rpc调用的trace数据、指标数据和日志数据,实现快速故障定位、链路梳理的功能,见图6的蓝色部分。
压测系统:提供自动压测能力,目的是找出各服务的性能瓶颈,见图6左上角。
容量管理系统:本系统结合trace数据、指标数据、压测数据和tnm2数据,实现精准的容量评估功能,见图6粉色部分。
名字服务:这个就不多说了。
链路跟踪的技术实现
Rpc调用场景说明
首先来看一个简单的rpc调用链场景(见图7),一个请求通过接入层cgi调用下游的svr1,然后svr1先调用服务svr2,拿到结果后再调用svr3,最后组合svr2和svr3的结果,通过cgi返回给用户。这里发生了3次rpc,我们用①②③④⑤⑥表示了RPC的顺序,怎么做到跟踪还原呢?
简单地说,天机阁利用rpc框架,在每次rpc的起点和终点进行数据上报。用Jstorm对上报的数据进行实时处理并存入hbase。管理端分析hbase数据进行可视化展示,以达到链路跟踪的目的。
trace上报数据说明
在天机阁跟踪树结构中,每个服务接口是一个节点,节点之间的连线就是span(跨度)。Span是链路跟踪系统中的主要数据结构,它记录了rpc主调节点和被调节点之间的关系。 Span包含数据如下:
1. traceID:一次业务请求会分配一个唯一的TraceID, rpc的时候,框架会通过带内数据的方式吧TraceID传递到下游svr,本请求涉及的所有rpc上报的span拥有同一个唯一的2. TraceID, 系统可以通过TraceID把所有rpc的span关联起来,形成一个span集合。
2. SpanID:span的ID,每个合并span在一个traceId下有唯一的SpanID。为了方便后续处理,一个rpc的“主调span”和“被调span”拥有相同的spanID,见图8相同颜色的span的spanID相同。
3. parentID:父span的id,rpc调用有层级关系,所以span作为调用关系的存储结构,也有层级关系,就像图8所示,跟踪链是采用跟踪树的形式来展现的,树的根节点就是调用的顶点。所以,顶级span的parentId=0,拿图8所展现的例子来说,cgi请求svr1的span就是顶级span,spanID=1,parentid=0。 svr1请求svr2的span是顶级span的子span,spanID=2,parentid=1。而svr1请求svr3的也是定级span的子span,spanID=3,parent=1。很显然span2和span3是平级关系。这样就可以把rpc集合还原成调用树了。
4. Event(事件):除了调用树,开发者还很关心rpc的时间关系以及耗时信息。 为此,我们定义了4个rpc日志事件:
a. client发送数据:client send简称cs
b. client收到回包:client recv简称cr
c. server收到数据:server recv简称sr
d. server发送回包:server send简称ss
“主调span”会包含cs和cr的时间点, “被调span”会上报sr和ss时间点。“合并span”拥有以上4个事件时间点。有了这些时间点,几乎所有阶段的耗时都可以计算出来。
1. Name: 接口名字,记录本次rpc调用是server的哪个接口。
2. Result:调用结果,rpc的返回值,一般0标识成功。
3. Caller:主调信息,包括主调svr名字,IP
4. Callee:被调信息,包括被调svr名字,IP、port
5. 其他信息: span结构体中,还会存储一些方便分析问题的其他信息,但这些信息不影响链路跟踪,这里就不详述了。
trace上报过程说明
说到这里,大家对span的印象可能还是有点模糊不清,于是我们继续拿图7的服务调用来举例,如果我们将图7的应用接入天机阁,将会看到图8的效果
一个完整span既包含client数据,也包含server数据,所以一个完整span要分两次上报。rpc的起点上报的数据称为“主调span”,rpc的终点上报数据称为“被调span”, “主调span”和“被调span”也叫子span。 子span在jstorm实时计算的时候,合并成“合并span”存储到hbase,上报过程见图8。图中一共3次rpc,共上报6个子span, 这6个子span在计算层合并成3个合并span, 详细过程如下:(前方高能预警,这个过程比较复杂,对上报过程不感兴趣的同学可以跳过)。
a. 第1个子span:cgi在发起rpc前,生成一个client span, traceId=111111, spanid=1, parented=0(没有父span)。并将这3个ID通过带内数据的方式传递给svr1。等cgi收到svr1的回包后,补全span的事件时间:cs=t1, cr=t12,并上报主调span。见图10中cgi上报的“主调span”。
b. 第2个子span:svr1从请求数据中解出client span, 生成一个“被调span”, “被调span”的三个ID跟起点span一样,traceId=111111, spanid=1, parented=0。 Svr1发送回包给cgi后,就补全时间时间sr=t2, ss=t11,并上报“被调span”,见图10中svr1上报的“被调span”。
c. 第3个子span:svr1在发起svr2的rpc前,生成一个client span, traceId=111111, spanid=2(每个“主调span”生成一个新的spanid), parented=1(以本svr的“被调span”的id当parentId)。并将这3个ID通过带内数据的方式传递给svr2。等cgi收到svr2的回包后,补全span的事件时间:cs=t3, cr=t6,并上报“主调span”。见图10中svr1上报的spanid=2的“主调span”。
d. 第4个子span:svr2参照第2步,上报spanid=2的被调span。
e. 第5个子span:svr1参照第3步,上报spanid=3的主调span。
f. 第6个子span:svr3参照第4步,上报spanid=3的被调span。
trace还原说明
天机阁可以从hbase中查出所有spanid=111111的span。 再通过spanid和praentID,可以还原调用树。还能计算各种耗时。 例如:
Cgi的请求总耗时 = span1.cr - span1.cs = t12 - t1
(注:span1指spanid=1的“合并span”)
图中①的网络耗时 = span1.sr - span1.cs= t2 – t1
Svr1的调用svr2的耗时 = span2.cr - span2.cs = t6-t3
Svr1的调用Svr3的耗时 = span3.cr - span3.cs = t10-t7
时间差修正
需要注意的是,span的时间戳精确到毫秒,各机器存在一定的时间误差。 这个误差会导致耗时计算不太准确。天机阁通过以下办法,在展示结果的时候进行通过以下两步进行时间修正(参见图9)。
1. 保证每个span的cs,sr,ss,cr四个时间点是严格顺序的,也就是保证t1 2. 若t1
t2=t1+((t4-t1)-(t3-t2))/2
t3=t4-((t4-t1)-(t3-t2))/2
注:以上修正是假设图9中①的耗时跟②的耗时相当。实践证明这个假设效果不错。
天机阁的架构跟阿里鹰眼2016年的架构类似, 分为数据采集、实时计算、数据存储、离线分析四层,详情如图10。
天机阁数据采集了trace数据、指标数据、业务日志三类数据。分别存储在hbase、habo、es和磁盘四种存储上,见图11。
1. trace数据:就是1所说的span数据,主要用于链路跟踪、还原。存储在hbase中。
2. 指标数据:包括接口纬度的成功数、失败数、返回码、耗时分布等指标数据,存储在habo系统中。
3. 业务日志:业务日志分冷、热两类,冷数据包括全量日志,存储在磁盘上。 热数据指链路跟踪被采样且发生错误的日志,热数据存储在es系统中。
这三个数据相互配合,可以较好的完成监控和故障定位。 首先,Jstorm实时计算“指标数据”,发现异常后生成告警信息, 开发同学点击告警信息,打开链路跟踪视图,可以找出故障点。 再查看跟告警相关的业务日志,就能大致定位到告警原因(参见图3)。
数据采集的重点是要做到低侵入和低开销,只有满足以上两个设计要点,业务才有可能选择接入天机阁。这里重点讨论一下天机阁的“trace数据”是怎么做到低侵入,低开销的。
低侵入:低侵入比较简单, 天机阁选择在srf框架底层做文章。增值产品部统一使用srf框架, 业务升级新的srf框架,仅重编就能无痛接入天机阁。
低开销:这里的开销是指“正在被监控的系统在生成追踪和收集追踪数据的消耗导致系统性能下降”,我们希望把这个开销降低到可以忽略的程度。 “trace数据”采集最大的开销在于创建和销毁span,根span的创建和销毁需要损耗平均205纳秒的时间,而同样的操作在其他span上需要消耗172纳秒。时间上的差别主要在于需要在根span上给这次跟踪分配一个全局唯一的ID。减少span的生成,能大大的降低开销。天机阁通过以下4个手段保证重要事件不错过的前提下,尽量降低开销。
1.采样上报:链路跟踪并不需要所有请求都上报, 天机阁一开始采样率为1/1024。这个简单的方案在高吞吐量的服务上非常有效。但是这个采样率在低吞吐量服务的上,容易错过重要事件,其实低吞吐量的服务能接受更高的采样率。 最后我们用一个采样期望率来标识单位时间内采样的追踪。这样一来,低流量低负载自动提高采样率,而在高流量高负载的情况下会降低采样率,使损耗一直保持在控制之下。(注意:这里的采样率要求一次请求的多次rpc要么都采样,要么都不采样,不然链路就串不起来, 天机阁在请求入口处进行采样, 采样标识通过带内数据往下游传递,保证一次请求用同一个采样标识。)
2.染色上报:天机阁支持染色上报机制,目前推荐用uin染色, 公司内部的号码都会被采样上报。
3.出错上报:链路跟踪的初衷是帮助开发者定位、分析问题, 那么请求出错就很有必要上报了。 出错上报需要注意两点。
a. 防止雪崩:如果后端服务故障,会导致前端所有请求都被上报,可能因为上报消耗大量的性能,导致服务雪崩。 为此,天机阁的数据上报设置了一个上限,每秒上报超过50条,就不再上报。
b. 逆向生成:为了降低开销,未采样的rpc是不会生成traceID和SpanID的。 若未采样的rpc发生错误,需要从后往前逆向构造调用关系。 这种情况天机阁会帮父span生成一个id,并通过回包把父spanid传递给主调。主调据此来构造调用关系。值得一提的是只有发生错误的分支才上报,未发生错误的分支会遗漏,跟踪树退化成一条跟踪线。不过这个退化后的跟踪线足够定位问题了。
4.共享内存无锁上报: 上报api将需要上报的Span通过jce序列化成二进制,写入本地共享内存无锁队列中,然后由agent来消费共享内存批量上报到kafka,共享内存无锁队列使用的是即通开源的高性能无锁共享内存队列,性能可达800w/s。 天机阁的无锁编程细节见KM文章:天机阁——说说无锁(Lock-Free)编程那些事。
性能损失验证:开启天机阁上报后,实测qps下降低于3%。测试server的逻辑是 读取cmem小数据(大约几个字节)返回,比较简单的操作。在cpu消耗差不多的情况下,qps下降2.9%左右,详细数据见下表。如果业务逻辑更复杂,这个操作的消耗占比会更低,性能损耗会更不明显。
为什么选择Jstorm
天机阁的计算层任务主要包括以下两点:
1. 把同一个rpc的起点span和终点span合并成一个合并span,并存储到hbase中。
2. 按分钟粒度的时间窗口统计指标数据,若发现异常,则通知告警服务。
其中第2点的实时性要求高,适合选用用流式计算引擎。通过下表的对比,当初我们选择了Jstorm(其实现在Flink在多个方面已经比Jstorm做得更好,我们计划后续替换才Flink)。
对比
Jstorm
Flink
Spark Streaming
窗口支持:
窗口支持:
有窗口支持:
容错方式:
检查点机制:参考flink实现
容错方式:
容错方式:
容灾性:
容灾性:
容灾性:
实时性:
实时性:
实时性:
处理性能:
处理性能:
处理性能:
Strom 的2~6倍
应用状态:
应用状态:
较少、美团、阿里搜索(Blink)
应用状态:
较成熟、实时推荐系统
实时计算的挑战
作为监控系统,需要做到实时性,一致性和确定性。
实时性比较好办,Jstorm天生就能满足此要求,目前天机阁可以在3分钟左右发出告警,比模调系统快2到3分钟。
所谓一致性,是指jstorm处理的数据与agent上报的数据一致。 要求系统具备自愈能力, 比如jstorm重启后,数据能够被重新计算,监控曲线不能掉下来。天机阁为保证一致性,采取了以下措施。
1. 多地部署,做到异地容灾。
2. 利用zookeeper保证master只有一个。
3. 启用ack机制,保证每条数据被正确的处理一次。
注:启用ack的弊端也很明显:jstorm内存消耗明显增大,jstorm处理性能也会下降。 目前天机阁的jstorm集群机器不足,暂时关闭了ack机制。
什么是确定性?举个例子,jstorm发现某一分钟的请求量突然陡降,这时候我应该发报警,还是不发报警,为什么我很难做这个抉择,因为我不知道监控的对象真的出了问题,还是监控系统本身的流计算部分出了问题,流计算系统是不是哪里卡住了,还是数据收集通道出现了问题。天机阁实现了一个齐全度算法来完成确定性保障,它与 Apache Flink 中采纳的 Snapshot 算法有些近似。天机阁上报的数据带有日志时间, Jstorm的时间窗口以日志时间为准,如果下一分钟的的日志已经超过一定数量,我们认为前一分钟的数据到齐,可以发出告警。
通过图11我们可以看出,链路跟踪主要存储三种数据:trace数据、指标数据、日志数据。
Trace数据的存储有以下要求,经过对比,我们选择用hbase来存储trace数据。
1. 容量大:目前每天上报量1T, 随和天机阁推广,还会倍增。
2. 生命周期:trace数据短时间有用,我们只存储最近10天的数据,希望过期数据能自动淘汰。
3. 高并发:trace数据写多读少,需要支持高并发写入的存储。
4. 支持按key读写:支持traceID为key存取数据。
指标数据的数据量更大,高峰期达到1亿条/分钟。为了方便查看,指标数据必须支持多维度赛选,我们最终选择habo来存储指标数据。
日志数据我们存储在硬盘中。 为了方便查询,我们把trace相关的热日志存储在es中。
指标数据的数据量更大,高峰期达到1亿条/分钟。为了方便查看,指标数据必须支持多维度赛选,我们最终选择habo来存储指标数据。
日志数据我们存储在硬盘中。 为了方便查询,我们把trace相关的热日志存储在es中。
容量评估的技术实现
原SNG服务部署大量使用虚拟机, 扩缩容流程重,时间长, 然而业务却经常搞大活动,微服务架构复杂,每次大活动前,都需要耗费开发资源进行架构梳理以及容量评估。 为了解决这个痛点,天机阁实现了两个容量评估方式。
基于入口的容量评估。
基于业务指标的容量评估。
天机阁能实现精准容量评估,得益于以下几个要点:
1. 链路跟踪:能自动梳理出某入口的后续依赖关系。
2. 压测系统:能获得各server的容量瓶颈。
3. 指标统计:各接口的实际qps等指标数据。
4. Tnm系统:这里可以查询各模块部署情况,和各机器的cpu消耗情况。
有了以上数据,我们能计算出各模块当前的容量是多少。 开发同学进行容量评估时,只需要指定入口模块的请求增量,天机阁就能结合链路跟踪,较准确的评估出后续各依赖模块的请求增量。评估出这些模块是否需要扩容,要扩容多少。 那么如何计算出各模块的请求增量?原理见图13。
上图是天机阁通过链路跟踪绘制的一个拓扑图。图中A、B、C、D……分别代表一个服务。
线条上的数字4w, 代表该接口被调的频率。 比如A服务,每秒被调用4万次。 其中A服务每秒调用D服务2万次, D服务总共被调3.8万次每秒。
图中绿色数字代表传导系数,比如A到D的传导系数是0.5。 因为A被请求4w次每秒,A就会主动调用D服务2万次每秒。 有了拓扑图和传导系数,那么就能很容易的根据入口评估出后续服务的请求增量。如图13, 假如A的请求增加2万/秒的请求量。 那么后续服务将会增加红色字体所描述的请求增量,见图14。
“企鹅电竞”是接入天机阁的第一个业务。 企鹅电竞有一个关键指标PCU——同时观看直播的用户数。 企鹅电竞的大部分服务请求量跟pcu数据正相关。天机阁为企鹅电竞专门开发了基于pcu的容量评估模型。图5是天机阁基于pcu的容量评估结果。详细评估过程间KM文章:天机阁容量评估设计与实现。
压测系统
天机阁的压测系统导现网流量进行压测,不需要开发构造请求,仅需要点击个启动按钮,就能自动完成压测,并生成详细的压测报告。报告包括压测结果详情(图16),性能趋势图(图17),压测结果环比(图18),以及其他的一些性能指标。详细压测方案见KM文章:压测系统使用指引。
实时告警
天机阁的告警有两个特点。
1. 实时性高:得益于jstorm的实时计算,天机阁的告警延时在2到3分钟。
2. 告警收敛:天机阁知道服务的依赖关系,所以可以做到告警收敛, 假如图14中的D服务故障,那么会影响到A、B、C三个服务也会异常。 天机阁只会生成一条告警,并把A、B、C与D的关系描述出来。如图19
总结&计划
传说中天机阁里有一台掌控时间一切的机器,万物运行由此产生。本文的“天机阁”增值产品部开发同学利用业余时间打造的监控系统,目标便是做到一切尽在掌握,开发人员能够通过“天机阁”洞察“天机”,快速解决问题。
目前天机阁在故障定位、容量评估、链路梳理方面达到了不错的效果,相当于阿里鹰眼2014年左右的水平,不过距离业界先进水平还有很大差距。革命尚未成功,同志仍需努力, 希望在19年有跟多的爱好者加入到天机阁的建设中来,完成以下计划,早日窥得“天机”。
1. 推广:与taf结合,把天机阁api集成到taf框架,方便天机阁进一步推广。
2. 多语言支持:支持go语言api。
3. 模块化:把采集、实时计算、持久化、告警等流程做成一个个积木块,业务可按需配置需要的模块,自定义数据处理流程。
4. 平台化:让业务可以在天机阁平台上开发自己的视图插件。
5. 全链路压测:按照业务的拓扑图,实现全链路压测。
6. 关联识别:把trace跟踪运维事件(版本发布、配置变更、网络故障等)关联,做到初级原因分析。
开源协同。