本来计划接着上文介绍Skywalking的架构,但是我想了一下,觉得还是有必要先把链路跟踪里面涉及的一些基础概念术语介绍一下,介绍这些Skywalking并不是一个比较好的选择。原因一方面是Skywalking只是众多APM实现之一,里面有些设计并不适合其它APM,另一方面Skywalking提供的比较好的探针多时Java的,而且是字节码注入的,不利于观察学习。当然最重要的是有一个更合适的选择:OpenTracing。

OpenTracing介绍

分布式请求/链路跟踪(Distributed Request Tracing)最早是Google内部在用,后来相对成熟以后,2010年对外发布了一篇论文:Dapper, a Large-Scale Distributed Systems Tracing Infrastructure,但没有将系统开源。接着就有一些公司和社区开始基于Dapper实现自己的链路跟踪系统,比较有名的有(这里只列举开源的):Twitter的Zipkin、韩国的PinPoint、大众点评的CAT,以及一些后起之秀:Uber的Jaeger,Apache Skywalking等。

在百花齐放的时候,出现了OpenTracing,关于它的介绍官方是这样说的:

What is OpenTracing?

It is probably easier to start with what OpenTracing is NOT.

  • OpenTracing is not a download or a program. Distributed tracing requires that software developers add instrumentation to the code of an application, or to the frameworks used in the application.
  • OpenTracing is not a standard. The Cloud Native Computing Foundation (CNCF) is not an official standards body. The OpenTracing API project is working towards creating more standardized APIs and instrumentation for distributed tracing.

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兼容性好。

OpenTracing 术语介绍

说明:

  • 基本上所有的链路跟踪系统以及OpenTracing里面术语大多都是来自于Dapper论文里面的。下面介绍的规范都是OpenTracing定义的一些规范
  • 为了方便理解和交流,概念术语等使用英文,不做翻译。

分布式链路跟踪系统的数据模型:

Traces(一般翻译为链路):一起请求从发出,然后经过多个模块(这个模块可能是函数或者系统,或者都有),最终得到请求回复,整个请求按照调用时间和关系串起来就是一个trace。

Span则是组成trace的最基本单元,它一般代表分布式系统中一个独立的工作单元。有点抽象,没关系,后面看一些例子就懂了。一个Span包含如下几部分:

  • 操作名称:一般用于展示、过滤、聚合
  • 开始和结束时间戳:用于计算耗时
  • 由key-value组成的Tags:用于添加一些时间无关的信息(可选)
  • 由key-value组成并包含时间戳的Logs:用于添加一些时间相关的信息(可选)
  • span上下文,即SpanContext。一般包含两部分数据:(1)span的状态数据,比如traceID和spanID(2)Baggage Items。Baggage是链路跟踪提供的一个通用的跨进程/服务传递数据的方式,格式也是key-value形式的。

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间有两种可能的关系:

  • ChildOf:即父子关系,也是最常见的关系。比如上图中的Span A和Span B、Span C就是父子关系,表示只有Span B和Span C(包括Span A和Span B的所有子Span)都完成了,Span A才能完成,类似于同步调用(仅仅是类似于,并不完全一样)。
  • FollowsFrom:其实也是父子关系,子Span是由父Span调用产生的,但父Span是否完成不依赖于子Span。比如图中的Span F和Span G就属于FollowsFrom关系。Span G由Span F调用创建,但Span F是否完成不依赖于Span G,有点类似于异步调用。

最后需要介绍的一个概念就是“active span”。一个线程里面可以包含多个span,但同一时刻只能有一个span处于工作状态,这个span称之为ActiveSpan。Span可以有这么几个状态:

  • Started
  • Not Finished
  • Not "active"
  • Active

Span的状态由ScopeManager管理,但是否实现由开发者决定。另外OpenTracing定义了Inject和Extract接口来简化SpanContext跨进程传递。

如果你是第一次了解分布式链路跟踪,看了上面这些,我相信你还是一头雾水,心里有很多疑问。没事,理论结合实践是掌握知识最佳的手段,先对这些概念有个大概理解和印象,然后看下面的几篇实战文章:

说明:这4篇文章内容主要翻译(意译)自Yurishkuro大神的opentracing-tutorial java,加了一些补充说明以及在Jaeger UI上面的展示,方便理解,习惯看英文的也可以看原文,代码自行从GitHub拉取。

通过这几篇文章,对于分布式链路跟踪基本概念和原理应该可以理解的比较好了。后面会介绍一些SDK如何写,以及一些具体的APM。

文章目录