Part 1 – 为什么须要 Tracing
以后微服务架构和分布式系统变得越来越风行,零碎宏大且服务数量繁冗多样,甚至数据库也开始应用分布式架构。当一个生产零碎面对真正的高并发,或者解耦成大量微服务时,以前很容易实现的重点工作变得艰难了。开发过程中须要面临一系列问题:用户体验优化、后盾实在谬误起因剖析,分布式系统内各组件的调用状况等。在这些问题的驱动下,Tracing 变成了分布式系统必不可少的组成部分。
Part 2 – Opentracing 的产生
当代分布式跟踪零碎(例如,Zipkin, Dapper, HTrace, X-Trace 等)旨在解决分布式系统下的用户体验优化、后盾实在谬误起因剖析、分布式系统内各组件的调用状况等问题,然而他们应用不兼容的 API 来实现各自的利用需要。只管这些分布式追踪零碎有着类似的 API 语法,但各种语言的开发人员仍然很难将他们各自的零碎(应用不同的语言和技术)和特定的分布式追踪零碎进行整合。
在这种背景下,opentracing 应运而生。Opentracing 通过提供平台无关、厂商无关的 API,使得开发人员可能不便的增加(或更换)追踪零碎。Opentracing 提供了用于经营撑持零碎的和针对特定平台的辅助程序库。
Part 3 – 什么是一个 trace
在狭义上,一个 trace 代表了一个事务或者流程在(分布式)零碎中的执行过程。在 Opentracing 规范中,trace 是多个 span 组成的一个有向无环图(DAG),每一个 span 代表 trace 中被命名并计时的连续性的执行片段。
分布式追踪中的每个组件都蕴含本人的一个或者多个 span。例如,在一个惯例的 RPC 调用过程中,Opentracing 举荐在 RPC 的客户端和服务端,至多各有一个 span,用于记录 RPC 调用的客户端和服务端信息。
一个父级的 span 会显示的并行或者串行启动多个子 span。在 Opentracing 规范中,甚至容许一个子 span 有个多父 span(例如:并行写入的缓存,可能通过一次刷新操作写入动作)。
Part 4 – Opentracing 概念与术语
Traces
一个 trace 代表一个潜在的,分布式的,存在并行数据或并行执行轨迹(潜在的分布式、并行)的零碎。一个 trace 能够认为是多个 span 的有向无环图(DAG)。
Spans
一个 span 代表零碎中具备开始工夫和执行时长的逻辑运行单元。span 之间通过嵌套或者顺序排列建设逻辑因果关系。
Operation Names
每一个 span 都有一个操作名称,这个名称简略,并具备可读性高。(例如:一个 RPC 办法的名称,一个函数名,或者一个大型计算过程中的子工作或阶段)。span 的操作名应该是一个形象、通用的标识,可能明确的、具备统计意义的名称;更具体的子类型的形容,请应用 Tags。
例如,假如一个获取账户信息的 span 会有如下可能的名称:
Inter-Span References
一个 span 能够和一个或者多个 span 间存在因果关系。Opentracing 定义了两种关系:ChildOf 和 FollowsFrom。这两种援用类型代表了子节点和父节点间的间接因果关系。将来,Opentracing 将反对非因果关系的 span 援用关系。(例如:多个 span 被批量解决,span 在同一个队列中,等等)
ChildOf 援用:
一个 span 可能是一个父级 span 的孩子,即 “ChildOf” 关系。在 “ChildOf” 援用关系下,父级 span 某种程度上取决于子 span。上面这些状况会形成 “ChildOf” 关系:
- 一个 RPC 调用的服务端的 span,和 RPC 服务客户端的 span 形成 ChildOf 关系
- 一个 sql insert 操作的 span,和 ORM 的 save 办法的 span 形成 ChildOf 关系
很多 span 能够并行工作(或者分布式工作)都可能是一个父级的 span 的子项,他会合并所有子 span 的执行后果,并在指定期限内返回
FollowsFrom 援用:
一些父级节点不以任何形式依赖他们子节点的执行后果,这种状况下,咱们说这些子 span 和父 span 之间是 “FollowsFrom” 的因果关系。”FollowsFrom” 关系能够被分为很多不同的子类型,将来版本的 Opentracing 中将正式的辨别这些类型
Logs
每个 span 能够进行屡次 Logs 操作,每一次 Logs 操作,都须要一个带工夫戳的工夫名称,以及可选的任意大小的存储构造。
规范中定义了一些日志(logging)操作的一些常见用例和相干的 log 事件的键值,可参考 Data Conventions Guidelines 数据约定指南。
Tags
每个 span 能够有多个键值对(key:value)模式的 Tags,Tags 是没有工夫戳的,反对简略的对 span 进行注解和补充。
和应用 Logs 的场景一样,对于应用程序特定场景已知的键值对 Tags,tracer 能够对他们特地关注一下。更多信息,可参考 Data Conventions Guidelines 数据约定指南。
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 运行过程传送的 Baggage)。鉴于全栈 Opentracing 集成的须要,Baggage 通过透明化的传输任意应用程序的数据,实现弱小的性能。例如:能够在最终用户的手机端增加一个 Baggage 元素,并通过分布式追踪零碎传递到存储层,而后再通过反向构建调用栈,定位过程中耗费很大的 SQL 查问语句。
Baggage 领有弱小性能,但同时也会有很大的耗费。因为 Baggage 的全局传输,如果蕴含的数量太大,或者元素太多,它将升高零碎的吞吐量或减少 RPC 的提早。
Baggage vs. Span Tags
Baggage 在全局范畴内,(随同业务零碎的调用)跨过程传输数据。Span 的 tag 不会进行传输,因为他们不会被子级的 span 继承。
span 的 tag 能够用来记录业务相干的数据,并存储于追踪零碎中。实现 Opentracing 时,能够抉择是否存储 Baggage 中的非业务数据,Opentracing 规范不强制要求实现此个性。
Inject and Extract
SpanContexts 能够通过 Injected 操作向 Carrier 减少,或者通过 Extracted 从 Carrier 中获取,跨过程通信数据(例如:HTTP 头)。通过这种形式,SpanContexts 能够逾越过程边界,并提供足够的信息来建设跨过程的 span 间关系(因而能够实现跨过程间断追踪)。
Part 5 – Opentracing 的应用
应用 opentracing 必须实现如下接口。
Span 接口
Span 接口必须实现以下性能:
GetContext:获取 span 上下文(即便 span 曾经完结,或者行将完结,也能够获取)。
Finish:实现曾经开始的 span。除了获取 span 上下文之外,Finish 必须是 span 实例最初被调用的办法。
SetTag:为 span 设置标签。key 必须是 string 类型;value 必须是 string、boolean 或者数值类型。
Log:减少一个日志事件。事件名称是 string 类型,参数值能够是任何类型、任何大小。实现不肯定保留参数值(可能只保留格式化后的日志字符串)。
SetBaggageItem:设置一个 string:string 类型的键值对,会传递到将来的子级 span 中。
BaggageItem:获取 Baggage 中的元素。
SpanContext 接口
用户能够通过 span 实例获取 SpanContext,或者应用提取 (Extract) 操作从 Tracer 实例中获取 SpanContext。
SpaContext 必须实现:
ForeachBaggageItem:迭代所有 Baggage 元素。
Tracer 接口
必须实现以下性能:
StartSpan:创立新的 span。调用者能够指定一个或者多个 span 间的关系、一个显式指定的开始工夫戳,以及标签集。
Injectspan context:用于跨过程传递数据,必须指定 Carrier。
Extractspan context:从 Carrier 中取出 span 上下文,可用于创立新的子级 span。(留神:有些实现中,span 在 RPC 两端具备雷同的 ID;而另一些实现中,客户端是父级 span,服务端是子级 span。)
全局和空 Tracer
每个实现必须提供一个 no-op Tracer:其实现必须不会出错(包含传递 Baggage),不会有任何副作用。在其上进行注入操作永远返回胜利;进行提取操作,则返回找不到 span 上下文。
Tracer 必须提供 no-no Span 实现:监控代码不依赖 Tracer 对于 Span 的返回值。
实现可能反对配置 (InitGlobalTracer ())和获取 (GlobalTracer ()) 全局的单例 Tracer。如果反对获取全局单例 Tracer,默认必须返回 no-op Tracer。
参考:
https://wu-sheng.gitbooks.io/…
https://www.jianshu.com/p/123…
https://zhuanlan.zhihu.com/p/…