分布式链路追踪

分布式链路追踪(Distributed Tracing),也叫 分布式链路跟踪,分布式跟踪,分布式追踪 等等。

本文使用分布式Trace来简称分布式链路追踪。

本篇文章只是从大致的角度来阐述什么是分布式Trace,以及一个分布式Trace系统具备哪些要点和特征。

场景

先从几个场景来看为什么需要分布式Trace

场景1

开发A编写了一段代码,代码依赖了很多的接口。一个调用下去没出结果,或者超时了,Debug之后发现是接口M挂了,然后找到这个接口M的负责人B,告知B接口挂了。B拉起自己的调用和Debug环境,按照之前传过来的调用方式重新Debug了一遍自己的接口,发现NND是自己依赖的接口N挂了,然后找到接口N负责人C。C同样Debug了自己的接口(此处省略一万个‘怎么可能呢,你调用参数不对吧’),最终发现是某个空判断错误,修复bug,转告给B说我们bug修复了,B再转告给A说,是C那个傻x弄挂了,现在Ok了,你试一下。

就这样,一个上午就没了,看着手头的需求越堆越高,内心是这样

image.jpg

场景2

哪一天系统完成了开发,需要进行性能测试,发现哪些地方调用比较慢,影响了全局。A工程师拉起自己的系统,调用一遍,就汇报给老板,时间没啥问题。B工程师拉起自己的系统,调用了一遍,也没啥问题,同时将结果汇报了给老板。C工程师这时候发现自己的系统比较慢,debug发现原来是自己依赖的接口慢了,于是找到接口负责人。。balabala,和场景1一样,弄好了。老板一一把这些都记录下来,满满的一本子。哪天改了个需求,又重新来一遍,劳民伤财。

解决方案

这两种场景只是缩影,假设这时候有这样一种系统,

image.jpg

它记录了所有系统的调用和依赖,以及这些依赖之间的关系和性能。打个比方,一个网页访问了应用M,应用M又分别访问了A,B,C,D四个应用,如下面这样的结构

image.png

那么在这个系统中就能够看到,一个网页Request了一个应用M,花费了多少时间,请求的IP是多少,请求的网络开销是多少。应用M执行时间是多久,是否执行成功,访问A,B,C,D分别花了多少时间,是否成功,返回了什么内容,测试是否通过。 然后到下一步,A,B,C,D四个应用本次执行的时间是多久,有没有超时,调用了多少次DB,每次调用花费了多少时间。

作为示例,给出一个阿里鹰眼的trace图:

image.png

trace就犹如一张大的json表,同一层级的数据代表同一层级的应用,越往下代表是对下层某个应用的依赖。从图中可以很方便的看到每一个应用调用的名称,调用花费的时间,以及是否成功。

下面这张图是我们使用微软的application insights生成的tracing图

image.png

有些敏感数据打上了马赛克,尽请谅解。不过还是可以清晰的看到应用之间的依赖关系,有处标红来代表此次调用出现了问题。

有了这个系统,场景1和场景2中的需求就能解决吗?如果有了分布式trace,这些场景中的问题又是怎么解决呢?

对于场景1中的case,开发A发现自己的接口挂了或者比较慢,而且Debug发现并不是自己代码的错误,这时候他找到自己的这一次trace,图中就会列出来这一次trace的所有依赖和调用,以及各调用之间的关系。A发现,自己调用的链路到N接口那里就断了,并且调用N接口返回500错误,于是A直接和N接口的负责人C联系,C立马修复了错误。

在A调用出错的时候,系统自动检测出在N接口出错,系统立马生成一份错误报告发到A和C的邮箱,A拿到报告的时候就直接能够知道那个环节出错了,而C拿到报告的时候发现,A在调用我的接口,并且我的接口出错了。这就是出错的主动通知。

对于场景2,项目开发完成了,或者有新的pull request merge到主分支了,触发了自动化测试。测试下来同样生成一张链路分析图,不管是开发,测试,DBA,还是老板,很容易从里面看到哪些应用的响应速度慢了,读取DB的时间慢了,接口挂了这些参数。再也不用一个一个搜集评测报告了。加快了持续集成和持续迭代。

image.jpg

分布式Trace关乎到的不仅仅是开发,运维,还有测试,DBA,以及你老板的工作量。

上面的例子只是一个缩影,如果一个公司内部存在成千上万个接口调用,到时候接口负责人都找不到的时候,时间成本和沟通成本无法想象。

标准

现有的分布式Trace基本都是采用了google 的Dapper标准。

google Dapper

Dapper的思想很简单,就是在每一次调用栈中,使用同一个TraceId将不同的server联系起来。

我们使用几张Dapper的图来简单说明下

依赖

首先来一张应用依赖图

image.png

就是这样一个调用链,一个用户请求了应用A,应用A需要请求应用B和应用C,而应用C需要请求应用D和应用E。

span

Dapper首先要做的就是规定Trace的结构和基本要素,如下图:

image.png

一次单独的调用链也可以称为一个span,dapper记录的是span的名称,以及每个span的ID和父ID,以重建在一次追踪过程中不同span之间的关系,上图中一个矩形框就是一个span,前端从发出请求到收到回复就是一个span。

再细化到一个span的内部,如下图:

image.png

对于一个特定的span,记录从Start到End,首先经历了客户端发送数据,然后server接收数据,然后server执行内部逻辑,这中间可能去访问另一个应用。执行完了server将数据返回,然后客户端接收到数据。

一个span的内容就能构成Trace上面的一个基本元素,可以在这个span中埋点打上各种各样的Trace类型,比如,一般将客户端发送记录成依赖(dependency),服务端接收客户端以及回复给客户端这两个时间统一记录成请求(request),如果打上这两种,那么在运行完这个span之后,日志库中就会多出两条日志,一条是dependency的日志,一条是request的日志。

现在的Trace SDK,都可以进行配置去自动记录一些事件,比如数据库调用依赖,http调用依赖,记录上游的请求等等,也可以自己手动埋点,在需要打上记录点的地方写上记录的代码即可。

结构

Dapper中给出的是一张这样的图


image.png

首先各个日志收集点按照一定的采样率将日志写进数据文件,然后通过管道将这些日志文件按照一定的traceId排定输出到BigTable中去。

如果一个系统完成了上面阐述的架构,基本可以构成一个简单的Trace系统。

traceId和parentId的生成

在整个过程中,TraceId和ParentId的生成至关重要。首先解释下TraceIdParentIdTraceId是标识这个调用链的Id,整个调用链,从浏览器开始放完,到A到B到C,一直到调用结束,所有应用在这次调用中拥有同一个TraceId,所以才能把这次调用链在一起。

既然知道了这次调用链的整个Id,那么每次查找问题的时候,只要知道某一个调用的TraceId,就能把所有这个Id的调用全部查找出来,能够清楚的知道本地调用链经过了哪些应用,产生了哪些调用。但是还缺一点,那就是链。

在java中有种数据结构叫LinkedList,还有种数据结构叫Tree,即通过父节点就能够知道子节点,或者通过子节点能够知道父节点是谁(双向链表),那么我想知道应用A调用了哪些应用,而又有哪些应用调用了应用A,单纯从TraceId里面根本看不出来,必须要指定自己的父节点才行,这就是ParentId的作用。

先来看一张常规的调用图


image.jpg

调用从一个浏览器发起,然后进入到微服务框架中,每一个服务都是一个独立的应用,应用之间通过RPC进行调用。

分布式trace有两个要求,1 是所有的一次调用链都采用一个traceId,2是能够记录这次调用时从哪里来的。

在这点上不同的产品有不同的实现方式。

可以想象,最简单的,就是在一开始浏览器请求的时候,定义两个字段(约定好的),比如一个叫TraceId,一个叫ParentId,放到http的header中,传递给应用A,应用A解析传递过来的字段,就知道了TraceIdParentId,即知道了本次调用链的Id,以及上一个应用的本次节点Id,然后就打上日志:某某时间应用A收到了一条请求,TraceId是XXX,它的ParentId是XX。

这样以后在查找问题的时候,先找到这次调用链的TraceId,发现有两个应用记录了这个Id,一个是前端的浏览器端记录过,一个是应用A记录过,并且链接关系是前端访问的应用A。

传递两个参数的方式简洁易懂,这有个不好的地方,就是每次需要传递两个参数,那么有没有一种方案能够将两个Id合并为一个Id呢?可以的。不同的产品实现是不一样的。

用上面的图作一定解析,(上面的图是阿里的鹰眼使用的Trace架构),首先为第一个发起这个请求的request分配一个根id,即TraceId,就是上面图中的0,这个0就是整个Trace中的TraceId,然后应用A拿到了这个号,再在这个0后面添加上0.1和0.2分配给A所请求的应用B和C,B跟C拿到0.1和0.2之后,便可以把这个Id作为ParentId,那么应用B怎么获取TraceId呢,很简单,只要把string split一下,取第一个值就行,这里取出来就是0. 所以在设计Id组成的时候,不要把分隔符设计进去,不然就不搞混的。

业内实现

openTracing是为了解决不同系统之间的兼容性设计的,现在也成为了各个第三方Trace系统的依赖的规范。

这是开源的产品

写在后面

这篇文章只是从最简单的方面去阐述分布式Trace,甚至连分布式都没有涉及。真正搭建一个分布式Trace,不仅仅需要定义结构,还需要保证日志的高可用,支持高并发和高性能。

比如阿里的鹰眼架构:

image.jpg

使用Storm集收集和分类日志数据,然后将简单分析完的数据一方面写进Hbase供实时查询,一方面将全量的日志写进HDFS,使用hadoop集群对这些数据进行统计计算,经过鹰眼的服务器把数据渲染展示出来。

可以看到,使用高可用的鹰眼中间件,即需要保证日志的写入不会丢失,又不会对现有的业务产生性能影响,选择合适的消息采样率至关重要。日志文件收集的数据库,需要保证在大并发的条件下依然能够顺利写入和读取。还需要保证不同的server之间的数据关联和事务保证。

这一系列的加起来,就是个庞大的系统了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,565评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,021评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,003评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,015评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,020评论 5 370
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,856评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,178评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,824评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,264评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,788评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,913评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,535评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,130评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,102评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,334评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,298评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,622评论 2 343

推荐阅读更多精彩内容