概述
在当今微服务架构替换单机系统,在以前单机系统中容易实现的一系列问题在微服务架构体系中变得非常困难,比如:用户体验优化、后台错误分析、分布式系统内各个组件之间的调用情况。 当代分布式跟踪系统(例如,Zipkin, Dapper, HTrace, X-Trace等)旨在解决这些问题,但是他们使用不兼容的API来实现各自的应用需求。尽管这些分布式追踪系统有着相似的API语法,但各种语言的开发人员依然很难将他们各自的系统(使用不同的语言和技术)和特定的分布式追踪系统进行整合。
而 OpenTracing
就完美的解决了这个问题,OpenTracing 通过提供平台无关、厂商无关的 API
,帮助开发人员能够方便地添加(或更换)追踪系统。
Trace
一个 trace
代表了一个事务或在分布式系统中的执行过程。在 OpenTracing
标准中,trace 是多个 span
组成的一个有向无环图(DAG),每一个span代表 trace
中被命名并计时的连续性的执行片段。
如下图所示代表了一次客户端请求的全过程:
如果将此次客户端请求的处理流程看作一条 Trace
,其中每一次调用,无论是 HTTP 调用、RPC 调用、存储访问还是我们比较关注的本地方法调用,都可以成为一个 Span
,通常如下图所示:
- 这整个调用就是一个
Trace
- 图中每个色块都是一个
Span
- 请求在进入后端
load balancer
之后- 首先会调用
authorization
服务处理, - 然后调用
billing
服务处理, - 最后执行
resource
服务,其中container start-up
和storage allocation
两步操作是并行执行的。
- 首先会调用
Span
Span
代表系统中具有开始时间和执行时长的逻辑单元,Span
之间通过嵌套或者顺序排列建立逻辑因果关系。就如上图中的每个色块就是一个 Span
每个 Span 中可以包含以下的信息:
-
操作名称
:例如访问的具体 RPC 服务,访问的 URL 地址等; -
起始时间
-
结束时间
-
Span Tag
:一组键值对构成的 Span 标签集合,其中键必须为字符串类型,值可以是字符串、bool 值或者数字; -
Span Log
:一组 Span 的日志集合; -
SpanContext
:Trace 的全局上下文信息; -
References
:Span 之间的引用关系
在一个 Trace 中,一个 Span 可以和多个 Span 间存在因果关系,目前 OpenTracing
定义了 ChildOf
和 FollowsFrom
两种 Span 之间的引用关系。这两种引用类型代表了子节点和父节点间的直接因果关系。
-
ChildOf
关系:一个 Span 可能是一个父级 Span 的孩子,即为 ChildOf 关系。下面这些情况会构成 ChildOf 关系:-
一个 HTTP 请求之中,被调用的服务端产生的 Span,与发起调用的客户端产生的 Span,就构成了 ChildOf 关系;
-
一个 SQL Insert 操作的 Span,和 ORM 的 save 方法的 Span 构成 ChildOf 关系。
-
-
FollowsFrom
关系:在分布式系统中,一些上游系统(父节点)不以任何方式依赖下游系统(子节点)的执行结果,例如,上游系统通过消息队列向下游系统发送消息。这种情况下,下游系统对应的子 Span 和上游系统对应的父级 Span 之间是 FollowsFrom 关系。下图展示了一些可能的 FollowsFrom 关系:
下面的示例 Trace 是由 8 个 Span 组成,其中 Span A 和 Span C 之间是 ChildOf 关系,Span F 和 Span G 之间是 FollowsFrom 关系:
上述tracer与span的时间轴关系
Logs
每个 Span 可以进行多次 Logs
操作,每一次 Logs 操作,都需要带一个时间戳
,以及一个可选的附加信息。如下图所示,其中不仅包括异常的堆栈信息,还包括了一些说明性的键值对信息
Tags
每个 Span 可以有多个键值对形式的 Tags,Tags 是没有时间戳的,只是为 Span 添加一些简单解释和补充信息。下图展示了前文示例中 Tags 的信息:
SpanContext
每个span必须提供方法访问SpanContext。SpanContext代表跨越进程边界,传递到下级span的状态。(例如,包含<trace_id, span_id, sampled>元组),并用于封装Baggage (关于Baggage的解释,请参考下文)。SpanContext在跨越进程边界,和在追踪图中创建边界的时候会使用。(ChildOf关系或者其他关系,参考Span间关系 )。
Baggage
Baggage是存储在SpanContext中的一个键值对(SpanContext)集合。它会在一条追踪链路上的所有span内全局传输,包含这些span对应的SpanContexts。在这种情况下,“Baggage"会随着trace一同传播,他因此得名(Baggage可理解为随着trace运行过程传送的行李)。鉴于全栈OpenTracing集成的需要,Baggage通过透明化的传输任意应用程序的数据,实现强大的功能。例如:可以在最终用户的手机端添加一个Baggage元素,并通过分布式追踪系统传递到存储层,然后再通过反向构建调用栈,定位过程中消耗很大的SQL查询语句。 Baggage拥有强大功能,也会有很大的消耗。由于Baggage的全局传输,如果包含的数量量太大,或者元素太多,它将降低系统的吞吐量或增加RPC的延迟。
核心接口语义
OpenTracing 希望各个实现平台能够根据上述的核心概念来建模实现,不仅如此,OpenTracing 还提供了核心接口的描述,帮助开发人员更好的实现 OpenTracing 规范。
-
Span 接口 Span接口必须实现以下的功能:
获取关联的 SpanContext
:通过 Span 获取关联的 SpanContext 对象。关闭(Finish)Span
:完成已经开始的 Span。添加 Span Tag
:为 Span 添加 Tag 键值对。添加 Log
:为 Span 增加一个 Log 事件。添加 Baggage Item
:向 Baggage 中添加一组键值对。获取 Baggage Item
:根据 Key 获取 Baggage 中的元素。
-
SpanContext 接口 SpanContext 接口必须实现以下功能,用户可以通过 Span 实例或者 Tracer 的 Extract 能力获取 SpanContext 接口实例。
- 遍历 Baggage 中全部的 KV。
-
Tracer 接口 Tracer 接口必须实现以下功能:
-
创建 Span
:创建新的 Span。 -
注入 SpanContext
:主要是将跨进程调用携带的 Baggage 数据记录到当前 SpanContext 中。 -
提取 SpanContext
:主要是将当前 SpanContext 中的全局信息提取出来,封装成 Baggage 用于后续的跨进程调
-