ffnercn 2019-02-16
传说中天机阁里有一台掌控世间一切的机器,万物运行由此产生。本文的“天机阁”是一个基于链路跟踪的监控系统,后台开发人员能够通过“天机阁”洞察“天机”,快速解决问题。
为了支撑日益增长的庞大业务量,业界大量使用微服务架构。服务按照不同的维度进行拆分,互联网应用构建在不同的软件模块集上,这些软件模块可能是由不同的团队开发、可能使用不同的编程语言来实现、可能布在了几千台服务器,横跨多个不同的数据中心,分布式系统变得日趋复杂。
如何快速进行故障定位?如何准确进行容量评估?如何动态展示服务的链路?如何进行系统性能优化?这是分布式系统给后台开发同学带来的四大挑战。业界都是通过链路跟踪系统来解决以上问题,然而腾讯在链路跟踪方面比较欠缺。为了填补此空白,“天机阁”系统横空出世。
“天机阁”通过采集、存储、分析分布式系统中的 trace 数据、指标数据和日志数据,完成全链路跟踪,从而解决上述问题。目前天机阁接入了 1200+ 个服务,trace 数据上报峰值 500 万 / 分钟,指标数据上报峰值 1.3 亿 / 分钟,日志数据每天 30T。如此海量的数据天机阁是怎样处理的?天机阁的链路跟踪是怎样实现的?又是怎样做到低侵入,低开销的?这些问题本文将一一为您揭秘。
微服务崛起让系统越来越复杂
如摘要所述,企鹅电竞也采用微服务架构。图 1 是企鹅电竞首页接口的依赖关系图,这张图大家可能看不清楚,看不清楚才是正常的,因为系统依赖的服务太多了。这种情况下一个请求涉及几十个服务,若其中某个关键服务出现了失败,只知道有异常,但具体的异常在哪个服务引起的就需要进入每一个服务里面看日志,这样的处理效率是非常低的。
图 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。
图 2:天机阁功能示意图
注:“调用事件数据”包括“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 粉色部分。
名字服务:这个就不多说了。
图 6: 天机阁总体架构
链路跟踪的原理
Rpc 调用场景说明
首先来看一个简单的 rpc 调用链场景(见图 7),一个请求通过接入层 cgi 调用下游的 svr1,然后 svr1 先调用服务 svr2,拿到结果后再调用 svr3,最后组合 svr2 和 svr3 的结果,通过 cgi 返回给用户。这里发生了 3 次 rpc,我们用①②③④⑤⑥表示了 RPC 的顺序,怎么做到跟踪还原呢?
简单地说,天机阁利用 rpc 框架,在每次 rpc 的起点和终点进行数据上报。用 Jstorm 对上报的数据进行实时处理并存入 hbase。管理端分析 hbase 数据进行可视化展示,以达到链路跟踪的目的。
图 7:简单的 rpc 调用场景
trace 上报数据说明
在天机阁跟踪树结构中,每个服务接口是一个节点,节点之间的连线就是 span(跨度)。Span 是链路跟踪系统中的主要数据结构,它记录了 rpc 主调节点和被调节点之间的关系。 Span 包含数据如下:
“主调 span”会包含 cs 和 cr 的时间点, “被调 span”会上报 sr 和 ss 时间点。“合并 span”拥有以上 4 个事件时间点。有了这些时间点,几乎所有阶段的耗时都可以计算出来。
trace 上报过程说明
说到这里,大家对 span 的印象可能还是有点模糊不清,于是我们继续拿图 7 的服务调用来举例,如果我们将图 7 的应用接入天机阁,将会看到图 8 的效果
图 8:span 上报细节
一个完整 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)。
t2=t1+((t4-t1)-(t3-t2))/2 t3=t4-((t4-t1)-(t3-t2))/2
注:以上修正是假设图 9 中①的耗时跟②的耗时相当。实践证明这个假设效果不错。
图 9:时间差修正
天机阁的架构跟阿里鹰眼 2016 年的架构类似, 分为数据采集、实时计算、数据存储、离线分析四层,详情如图 10。
图 10:天机阁链路跟踪架构
数据采集
天机阁数据采集了 trace 数据、指标数据、业务日志三类数据。分别存储在 hbase、habo、es 和磁盘四种存储上,见图 11。
图 11:天机阁数据采集架构
这三个数据相互配合,可以较好的完成监控和故障定位。 首先,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% 左右,详细数据见下表。如果业务逻辑更复杂,这个操作的消耗占比会更低,性能损耗会更不明显。
图 12: 性能验证表
实时计算
为什么选择 Jstorm
天机阁的计算层任务主要包括以下两点:
其中第 2 点的实时性要求高,适合选用用流式计算引擎。通过下表的对比,当初我们选择了 Jstorm(其实现在 Flink 在多个方面已经比 Jstorm 做得更好,我们计划后续替换才 Flink)。
实时计算的挑战
作为监控系统,需要做到实时性,一致性和确定性。
实时性比较好办,Jstorm 天生就能满足此要求,目前天机阁可以在 3 分钟左右发出告警,比模调系统快 2 到 3 分钟。
所谓一致性,是指 jstorm 处理的数据与 agent 上报的数据一致。 要求系统具备自愈能力, 比如 jstorm 重启后,数据能够被重新计算,监控曲线不能掉下来。天机阁为保证一致性,采取了以下措施。
注:启用 ack 的弊端也很明显:jstorm 内存消耗明显增大,jstorm 处理性能也会下降。 目前天机阁的 jstorm 集群机器不足,暂时关闭了 ack 机制。
什么是确定性?举个例子,jstorm 发现某一分钟的请求量突然陡降,这时候我应该发报警,还是不发报警,为什么我很难做这个抉择,因为我不知道监控的对象真的出了问题,还是监控系统本身的流计算部分出了问题,流计算系统是不是哪里卡住了,还是数据收集通道出现了问题。天机阁实现了一个齐全度算法来完成确定性保障,它与 Apache Flink 中采纳的 Snapshot 算法有些近似。天机阁上报的数据带有日志时间, Jstorm 的时间窗口以日志时间为准,如果下一分钟的的日志已经超过一定数量,我们认为前一分钟的数据到齐,可以发出告警。
存储选型
通过图 11 我们可以看出,链路跟踪主要存储三种数据:trace 数据、指标数据、日志数据。
Trace 数据的存储有以下要求,经过对比,我们选择用 hbase 来存储 trace 数据。
指标数据的数据量更大,高峰期达到 1 亿条 / 分钟。为了方便查看,指标数据必须支持多维度赛选,我们最终选择 habo 来存储指标数据。
日志数据我们存储在硬盘中。 为了方便查询,我们把 trace 相关的热日志存储在 es 中。
容量评估的技术实现
原 SNG 服务部署大量使用虚拟机, 扩缩容流程重,时间长, 然而业务却经常搞大活动,微服务架构复杂,每次大活动前,都需要耗费开发资源进行架构梳理以及容量评估。 为了解决这个痛点,天机阁实现了两个容量评估方式。
基于入口的容量评估
天机阁能实现精准容量评估,得益于以下几个要点:
有了以上数据,我们能计算出各模块当前的容量是多少。 开发同学进行容量评估时,只需要指定入口模块的请求增量,天机阁就能结合链路跟踪,较准确的评估出后续各依赖模块的请求增量。评估出这些模块是否需要扩容,要扩容多少。 那么如何计算出各模块的请求增量?原理见图 13。
图 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。
图 14:容量评估模型
基于入口的容量评估简单,精准,图 15 是天机阁的实践评估结果。
图 15:企鹅电竞某活动容量评估结果
基于业务指标的容量评估
“企鹅电竞”是接入天机阁的第一个业务。 企鹅电竞有一个关键指标 PCU——同时观看直播的用户数。 企鹅电竞的大部分服务请求量跟 pcu 数据正相关。天机阁为企鹅电竞专门开发了基于 pcu 的容量评估模型。图 5 是天机阁基于 pcu 的容量评估结果。详细评估过程间KM文章:天机阁容量评估设计与实现。
压测系统
天机阁的压测系统导现网流量进行压测,不需要开发构造请求,仅需要点击个启动按钮,就能自动完成压测,并生成详细的压测报告。报告包括压测结果详情(图16),性能趋势图(图17),压测结果环比(图 18),以及其他的一些性能指标。详细压测方案见 KM 文章:压测系统使用指引。
图16:压测结果详情
图17:压测性能趋势
图 18:压测结果环比
实时告警
天机阁的告警有两个特点。
图 19:告警收敛
传说中天机阁里有一台掌控时间一切的机器,万物运行由此产生。本文的“天机阁”增值产品部开发同学利用业余时间打造的监控系统,目标便是做到一切尽在掌握,开发人员能够通过“天机阁”洞察“天机”,快速解决问题。
目前天机阁在故障定位、容量评估、链路梳理方面达到了不错的效果,相当于阿里鹰眼 2014 年左右的水平,不过距离业界先进水平还有很大差距。革命尚未成功,同志仍需努力, 希望在 19 年有跟多的爱好者加入到天机阁的建设中来,完成以下计划,早日窥得“天机”。