以下是小马整理总结的入门理解笔记,助于入门和理解分布式链路追踪,opentracing(开放分布式追踪) + jaeger。
一、背景
举个例子,一个场景下,一个请求进来,入口服务是 serviceA, serviceA 接到请求后访问数据库读取用户数据,然后向 serviceB 发起 rpc,serviceB 收到 rpc 请求时同时向后端服务 serviceC 和 serviceD 发起请求,等待请求回复后再返回 serviceA 的 rpc 调用。如果我们发现发起的请求失败,或者请求的时延很大,我们该如何去定位呢?
基于这个需求,我们将服务介入追踪系统。相比单体的系统,在分布式,微服务系统中似乎更能发挥重要性。
二、OpenTracing
opentracing(中文文档)是一套分布式追踪协议,与平台,语言无关,统一接口,方便开发接入不同的分布式追踪系统。
语义规范: 描述定义的数据模型 Tracer,Span和 SpanContext 等;
语义惯例: 罗列出 tag 和 logging 操作时,标准的key值;
trace和span:
小马把它通俗理解为这两为追踪系统的数据结构。用于传递链路调用中的信息。
三、Jaeger
那么这个数据结构/协议怎么在整个系统调用链路中穿梭呢?借助Jaeger来看看。
Jaeger (ˈyā-gər) 是Uber开源的一个基于Go的分布式追踪系统,受启发于 dapper 和 OpenZipkin,兼容 OpenTracing 标准,CNCF的开源项目。(这段百科是没有的哈)
组成部分
和上图一个意思
什么意思呢?借用一下别的朋友整理的内容来看。整个过程说起来非常简单,Client写入trace数据,传递到正在监听的Agent,Agent将span等数据发到Collector, Collector将数据存储,后面就是提供UI和query了。
四、代码中实现
如何部署呢?集成后镜像安装比较简单,这里就不赘述了。GO语言为例,来看下调用,以下样例代码来自其他文章,仅供参考理解,非小马亲践,感谢作者分享,原文传送门。
小马理解为就是在解决span数据的传递。
1、单体中通过上下文调用
关键代码
2、跨应用通过grpc中间件使用
在单体程序中, 父子Span通过context关联, 而context是在内存中的, 显而易见这样的方法在垮应用的场景下是行不通的。跨应用通讯使用的方式通常是"序列化", 在jaeger-client-go库中也是通过类似的操作去传递信息, 它们叫:Tracer.Inject() 与 Tracer.Extract()。
其中inject方法支持将span系列化成几种格式:
Binary: 二进制
TextMap: key=>value
HTTPHeaders: Http头, 其实也是key=>value
正好grpc支持传递metadata也是string的key=>value形式, 所以我们就能通过metadata实现在不同应用间传递Span了。
写好服务端,客户端后,在业务代码中封装方法监控到程序中的代码片段(如方法):
关键代码
调用:
可见,使用trace会入侵部分代码,特别是追踪一个方法,但这是不可避免的。甚至需要每个方法都需要添加上ctx。
思考:日志流水号是如何传递的,每条链路全局name还是span父级名称还是span中的某个数据?