本来计划接着上文介绍Skywalking的架构,但是我想了一下,觉得还是有必要先把链路跟踪里面涉及的一些基础概念术语介绍一下,介绍这些Skywalking并不是一个比较好的选择。原因一方面是Skywalking只是众多APM实现之一,里面有些设计并不适合其它APM,另一方面Skywalking提供的比较好的探针多时Java的,而且是字节码注入的,不利于观察学习。当然最重要的是有一个更合适的选择:OpenTracing。
分布式请求/链路跟踪(Distributed Request Tracing)最早是Google内部在用,后来相对成熟以后,2010年对外发布了一篇论文: Dapper, a Large-Scale Distributed Systems Tracing Infrastructure ,但没有将系统开源。接着就有一些公司和社区开始基于Dapper实现自己的链路跟踪系统,比较有名的有(这里只列举开源的):Twitter的Zipkin、韩国的PinPoint、大众点评的CAT,以及一些后起之秀:Uber的Jaeger,Apache Skywalking等。
在百花齐放的时候,出现了OpenTracing,关于它的介绍官方是这样说的:
It is probably easier to start with what OpenTracing is NOT.
OpenTracing is comprised of an API specification, frameworks and libraries that have implemented the specification, and documentation for the project. OpenTracing allows developers to add instrumentation to their application code using APIs that do not lock them into any one particular product or vendor.
我简单概括一下,OpenTracing制定了一些链路跟踪的API规范,并且提供了一些框架和库,这些框架和库实现了它制定的那些API规范。而且它是一个独立开放的项目,现在已经是云原生基金会(Cloud Native Computing Foundation, CNCF)的项目了。任何组织和个人都可以贡献符合API规范的库/框架。虽然OpenTracing不是一个标准规范,但现在大多数链路跟踪系统都在尽量兼容OpenTracing。
需要重点说明的是OpenTracing提供的框架和库只是采集最原始的链路数据,并不做分析。如果放到Skywalking的架构中,它只实现了探针部分。也就是OpenTracing并不是一个完备的链路系统,所以我们无法单独使用,必须配合兼容OpenTracing规范的系统使用,比如Jaeger、LightStep、Apache Skywalking、Elastic APM等。
最新的进展是OpenTracing已经和CNCF的另外一个项目 OpenTelemetry 合并了。
题外话:虽然APM各个系统号称兼容OpenTracing,但兼容性到底如何,其实还是参差不齐的,比如Jaeger就比Skywalking兼容性好。
说明:
分布式链路跟踪系统的数据模型:
Traces(一般翻译为链路):一起请求从发出,然后经过多个模块(这个模块可能是函数或者系统,或者都有),最终得到请求回复,整个请求按照调用时间和关系串起来就是一个trace。
Span则是组成trace的最基本单元,它一般代表分布式系统中一个独立的工作单元。有点抽象,没关系,后面看一些例子就懂了。一个Span包含如下几部分:
Trace就是由若干个span组成的有向无环图,图中的每个节点就是Span,连接节点的边称之为 References 。每个trace有一个唯一标识符traceID,每个span也有一个唯一标识符spanID。一个链路中的所有span的traceID是相同的,但spanID各不相同。一个链路中span典型的调用关系图如下:
Causal relationships between Spans in a single Trace [Span A] ←←←(the root span) | +------+------+ | | [Span B] [Span C] ←←←(Span C is a `ChildOf` Span A) | | [Span D] +---+-------+ | | [Span E] [Span F] >>> [Span G] >>> [Span H] ↑ ↑ ↑ (Span G `FollowsFrom` Span F)
对应的时间维度为:
Temporal relationships between Spans in a single Trace ––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–> time [Span A···················································] [Span B··············································] [Span D··········································] [Span C········································] [Span E·······] [Span F··] [Span G··] [Span H··]
其实很简单,就是个调用关系。需要说明的是一个trace的span间有两种可能的关系:
最后需要介绍的一个概念就是“active span”。一个线程里面可以包含多个span,但同一时刻只能有一个span处于工作状态,这个span称之为 ActiveSpan 。Span可以有这么几个状态:
Span的状态由 ScopeManager 管理,但是否实现由开发者决定。另外OpenTracing定义了Inject和Extract接口来简化SpanContext跨进程传递。
如果你是第一次了解分布式链路跟踪,看了上面这些,我相信你还是一头雾水,心里有很多疑问。没事,理论结合实践是掌握知识最佳的手段,先对这些概念有个大概理解和印象,然后看下面的几篇实战文章:
说明:这4篇文章内容主要翻译(意译)自Yurishkuro大神的 opentracing-tutorial java ,加了一些补充说明以及在Jaeger UI上面的展示,方便理解,习惯看英文的也可以看原文,代码自行从GitHub拉取。
通过这几篇文章,对于分布式链路跟踪基本概念和原理应该可以理解的比较好了。后面会介绍一些SDK如何写,以及一些具体的APM。