Tracing 的由来
随着微服务技术的发展与实践,越来越多应用从单体应用架构转向了微服务架构。微服务技术使得应用能够很容易地突破单机资源局限,转变为分布式应用系统,让应用能够承载更大的流量。微服务应用系统的架构设计里团队的每个开发者只需要关注自己熟悉的几个微服务的业务代码。这也让软件行业实现了福特流水线的生产模式,长期以往,每个生产步骤(微服务)都会由最熟练的工人(开发者)进行生产。
但是在软件行业没有银弹,在我们享受着微服务架构设计的好处的同时,也需要成承担微服务架构设计的一些副作用。例如微服务架构设计会让应用复杂程度增加,应用的缺陷排查变得更加困难;如单次的服务请求调用链变长,服务使用者和服务提供者无法知道请求的全貌,对于有性能问题的调用,也没有很好的办法定位导致性能问题的代码位置。
为了解决上述问题,谷歌在 2010 年发表了论文 《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》 ,阐述了 google 内部实践的分布式链路追踪系统(distributed systems tracing infrastructure) Depper 的设计及其工作方式。这也成为了后来其他 Tracing 实现的范本。
Tracing 数据模型
由于 Tracing 数据模型基本都是基于谷歌 Dapper 论文而来,概念都大同小异。所以就以 OpenTracing 官方文档的数据模型作为引入。
OpenTracing 中的 Trace (调用链)通过归属于此调用链的 Span 来隐性的定义。 特别说明,一条 Trace (调用链)可以被认为是一个由多个 Span 组成的有向无环图(DAG图), Span 与 Span 的关系被命名为 References 。
例如:下面的示例 Trace 就是由8个 Span 组成:
单个Trace中,span间的因果关系
[Span A] ←←←(the root span)
|
+------+------+
| |
[Span B] [Span C] ←←←(Span C 是 Span A 的孩子节点, ChildOf)
| |
[Span D] +---+-------+
| |
[Span E] [Span F] >>> [Span G] >>> [Span H]
↑
↑
↑
(Span G 在 Span F 后被调用, FollowsFrom)
有些时候,使用下面这种,基于时间轴的时序图可以更好的展现 Trace (调用链):
单个Trace中,span间的时间关系
––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–> time
[Span A···················································]
[Span B··············································]
[Span D··········································]
[Span C········································]
[Span E·······] [Span F··] [Span G··] [Span H··]
每个 Span 包含以下的状态:(译者注:由于这些状态会反映在OpenTracing API中,所以会保留部分英文说明)
- An operation name,操作名称
- A start timestamp,起始时间
- A finish timestamp,结束时间
- Span Tag ,一组键值对构成的Span标签集合。键值对中,键必须为string,值可以是字符串,布尔,或者数字类型。
- Span Log ,一组span的日志集合。 每次log操作包含一个键值对,以及一个时间戳。 键值对中,键必须为string,值可以是任意类型。 但是需要注意,不是所有的支持OpenTracing的Tracer,都需要支持所有的值类型。
- SpanContext ,Span上下文对象 (下面会详细说明)
- References (Span间关系),相关的零个或者多个Span( Span 间通过 SpanContext 建立这种关系)
每一个 SpanContext 包含以下状态:
- 任何一个OpenTracing的实现,都需要将当前调用链的状态(例如:trace和span的id),依赖一个独特的Span去跨进程边界传输
- Baggage Items ,Trace的随行数据,是一个键值对集合,它存在于trace中,也需要跨进程边界传输
更多 OpenTracing 相关定义请参考 OpenTracing语义标准
Tracing 的架构设计
-
先来看看目前市面上比较流行的几个 Tracing 组件架构设计
-
Zipkin Tracing 实现架构图
- Jaeger Tracing 实现架构图
- Skywalking Tracing 实现架构图
-
可以看出来 Tracing 的实现架构一般都是大同小异,一般都实现了四个基本功能组件: Instrument, Reporter, Collector, Querier。
-
Instrument
一个 Instrument 是对特定开源组件做 Span 打点后并转给 Reporter 的实现,但是一般 Tracing 不自己实现 Instrument,而是提供产生 Span 的工具,由对应开源组件的维护者编写对应的 Instrument。所以这部分一般不会在 Tracing 架构图出现。
在各个 Tracing 的官方文档里面,搜索 instrument 关键字,都可以找到其对应的已经实现过的 instruments。
-
Reporter
- 作为一个组件在微服务里面运行,主要功能包括在微服务里面采集 Span 信息,在不影响或尽量少影响微服务性能的情况下发送 Span 信息给 Collector。
- 在 Zipkin 中对应 reporter,在 Jaeger 中对应 jeager-client 和 jaeger-agent,在 Skywalking 中对应 各种 agents。
-
Collector
- 一般是独立的服务,用于收集 Reporter 发过来的 Span 信息并持久化(落库)。一般来说,Collector 收集到的 Span 信息是离散的,很可能还没办法组合成一个完整的 Trace 就落库了。
- 在 Zipkin 中对应 Collector,在 Jaeger 中对应 jaeger-collector,在 Skywalking 中对应 Reveiver/Aggregator Cluster。
-
Querier
- 一般与 Collector 在同一个服务里面,主要实现在持久化存储中把分散的 Span 聚合为完整的 Trace 展示给使用者。
- 在 Zipkin 中对应 API + UI,在 Jaeger 中对应 jaeger-query + UI,在 Skywalking 中对应 Aggregator + UI。
-
从 Tracing 到 OpenTracing
从 2010 年谷歌发布 Dapper papper 以来,出现许多的 Tracing 开源项目实现,如 Zipkin/Skywalking/Jaeger/Pinpoint 等。一般来说 Tracing 开源项目他们只提供 Instrument 的接入方式,但是却不提供 Instrument 的具体实现,如果其他开源项目想接入 Tracing ,则是需要这些开源项目的开发者们自己去实现对应的 Instrument。即使一些 Tracing 开源项目的开发者们直接支持了一些开源组件的 Instrument(如 Pinpoint),也可能很快由于其他开源项目的架构升级,导致原来的 Instrument 实现不可用。
如果市场只有一个 Tracing 还好,但是百花齐放的 Tracing 开源实现会让开源组件的开发者们并不是很乐于去实现某个 Tracing 的 Instrument。而且开源组件的每次大版本升级,都有可能导致原先的 Instrument 无法工作。基于这个思考, 2016 年 Ben Sigelman 发表博客 《Towards Turnkey Distributed Tracing》,提出了 OpenTracing 这个概念。
OpenTracing 做了什么事情
Ben Sigelman 博文中列举 Tracing 中需要做统一的五个关键点,分别为:
- Standardized span management: 统一的 Span 的开始/信息记录/结束接口
- Standardized inter-process propagation: 统一的 Trace 在不同线程/协程间的上下文(SpanContext)的传递接口
- Standardized active span management: 统一的线程/协程内对 Span 的生成与获取接口
- Standardized in-band context encoding: 统一的服务内上下文(SpanContext)的传递接口
- Standardized out-of-band trace data encoding: 统一的服务外上下文(SpanContext)的传递接口
现有的 Tracing 开源项目都是基于谷歌的 Dapper 论文做的实现。里面更多的关注了第4点和第5点。然而实际的情况是第4点与第5点的标准化对于 Instrument 持续可用性是没有什么很大帮助的。而 1-3 点的标准化对于推动其他开源项目加入 Tracing 产生帮助更大,所以 OpenTracing 主要做的事情就是定义一组标准的 OpenTracing API 来实现 1-3 的标准化。
所以 OpenTracing 其实并没有实现 Tracing,而是定义了开源项目接入 Tracing 的标准,让开源软件更加容易地接入 Tracing