OpenTracing 语义说明

2018-03-02  本文已影响0人  zhexuany

最近在为 TiDB 加一个 tracing 的工具。 虽然 TiDB 已经开始使用 OpenTracing 工具了,但是还远远不够。没有做到全链路追踪,没法知道某个具体的 query 在那些节点慢。 在研究过程中,需要熟练掌握 OpenTracing 的概念,也就是这篇 semantic specification。 在这篇文章里规定了 不同语言间 OpenTracing 需要实现的函数,类型。

因为 OpenTracing 是跨语言的,按照标准实现的时候,需要尽可能的根据通用的语言概念,而不能局限于某一个具体的语言特性。这也解释了这篇文档的必要性。

The OpenTracing 数据模型

首先需要阐明的是 Span 和 trace 的概念。 用图论的观点来看的话,traces 可以被认为是 spans 的 DAG。也就是说,对个 spans 形成的 DAG 是一个 Traces。

举例来说,下图是一个由八个 Spans 形成的一个 Trace。

单个 Trace 中 Span 之间的因果关系


        [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)

某些时候, 用时间顺序来具象化更让人理解。下面就是一个例子。

单个 Trace 中 Spans 之间的时间关系

––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–> time

 [Span A···················································]
   [Span B··············································]
      [Span D··········································]
    [Span C········································]
         [Span E·······]        [Span F··] [Span G··] [Span H··]

每个 Span 包含一些状态:

每个 SpanContext 包含以下状态:

References between Spans

一个 Span 可能, 因因果相关,指向0个或者多个其他的 SpanContexts。 目前来说, OpenTracing 仅仅定义了两种关系: ChildOfFollowsForm。如同字面上可以猜测到的, ChildOf 将成为当前 Span 的 child 而 FollowsFrom则会成为 parent。 这两种关系为 child spanparent span 建立了直接因果关系。

ChildOf 引用: 某个 Span 可以是 ChildOfparent Span。在一个 ChildOf 的引用中, parent Span, 在某种程度上取决于child Span。 下面列举能形成 ChildOf 关系的条件:

  [-Parent Span---------]
         [-Child Span----]

    [-Parent Span--------------]
         [-Child Span A----]
          [-Child Span B----]
        [-Child Span C----]
         [-Child Span D---------------]
         [-Child Span E----]

FollowsFrom 引用: 一些 parent Spans 并不依赖 child Span 的结果。如果是这种情况, 那么我们基于因果关系上说Child Span FollowsFrom parent Span。这里,有很多唯一的 FollowsFrom 引用的子类别。这些会在以后被更加正式的定义。

这些是有效的,基于时间顺序的 FollowFrom 引用:

    [-Parent Span-]  [-Child Span-]


    [-Parent Span--]
     [-Child Span-]


    [-Parent Span-]
                [-Child Span-]

OpenTracing API

在 OpenTracing 有着三个关键的并且相互关联的类型: Tracer, Span, SpanContext。下面,我们来介绍下每种类型的基本行为。 简单地说,每种行为都会在具体的语言中变为一个“方法”,though it may actually be a set of related sibling methods due to type overloading and so on.

在不同语言中,对于 “Optional” 参数的理解是不一样的。 举例来说, Go 里 我们会使用 “functional Options”,但是 Java 里可能会使用 builder 模式。

Tracer

Tracer interface 创造 Spans 并且理解 如何 Inject (serialize) and Extract (deserialize) them across process boundaries. Formally, it has the following capabilities:

Start a new Span

要求的参数

举例来说,假设我们需要获得账户(account)信息, 下面是一些对于一个 Span 可能的 operation names:

Operation Name 指导
get 过于一般
get_account/792 过于具体
get_account 赞, 并且 account_id=792 会是一个很好的 Span tag

可选择的参数:

返回 一个 已经开始的 Span 实例 (但是尚未结束)

Inject a SpanContext into a carrier

要求参数

Extract a SpanContext from a carrier

要求参数

**返回一个 SpanContext 实例。 当我们想要通过 Tracer 开始一个新的 Span, 这个实例是可以被用来当做一个。

Note: required formats for injection and extraction

injection 和 extraction 都依赖于一个可扩展的 **format 参数。 这个参数规定了关联 “carrier” 的类型,同时也描述了一个 SpanContext 是如何被编码进入这个 carrier 的。 所有下面的 formats 都必须被所有的 Tracer 实现支持:

Span

除去 将 Span's SpanContext 取回的函数,下面任何一个函数都被不会在 Span 结束后被调用。

Retrieve the Spans SpanContext

没有任何参数。

返回 特定 SpanContext,对于给定的 Span. 返回值可能在 Span结束后仍被使用。

Overwrite the operation name

要求参数。

Finish the Span

可选参数

除去 将 Span's SpanContext 取回的函数,下面任何一个函数都被不会在 Span 结束后被调用。

Set a Span tag

要求参数

Note that the OpenTracing project documents certain "standard tags" that have prescribed semantic meanings.

Log structured data

要求参数

可选参数

Note that the OpenTracing project documents certain "standard log keys" which have prescribed semantic meanings.

Set a baggage item

Baggage items 是 key:value 的 string 对。key:value 对适用于给定的 Span, 它的 SpanContext, 以及有着直接或者间接 引用关系(reference) 的所有的 Spans。也就是说,baggage items 在随着 trace 一起在频内传播(propagate in-band along with trace itself)。

对于一个 full-stack OpenTracing 集成来说,Baggage items 可实现强大的功能(例如,来自移动应用程序的任意应用程序数据可以透明地、深度地进入存储系统中)中,并带来有一些巨大的成本:请小心驾驶。

谨慎小心地使用此功能。每个键和值都被复制到相关Span的每个本地和远程子元素中,并且这会增加很多网络和CPU开销。

要求参数

Get a baggage item

要求参数

Returns the corresponding baggage value, xor some indication that such a value was missing.

SpanContext

SpanContext更像是一个“概念”,而不是通用 OpenTracing 层的有用功能。也就是说,这对于 OpenTracing 实现非常重要,并且确实呈现了一个自己的瘦API。大多数 OpenTracing 用户只能在启动新的 Spans 时通过引用*与 SpanContext 进行交互。或者在某些传输协议中注入/提取跟踪。

在 OpenTracing 中,我们强制 SpanContext 实例是不可变,以避免 Span 完成和引用周围的复杂生命周期问题。

Iterate through all baggage items

这取决于语言以不同的方式进行建模,但在语义上,调用者应该能够在给定'SpanContext`实例的情况下一次遍历所有的 baggage items。

NoopTracer

所有实现 OpenTracing 语言 API 还必须提供某种 “NoopTracer” 实现,可用于标记控制 OpenTracing 或为测试注入无害的东西(等等)。在某些情况下(例如Java),“NoopTracer” 可能在它自己的打包 artifact 中

Optional API Elements

某些语言还提供实用程序来在单个进程中传入一个活动的“ Span” 和/或 “SpanContext”。例如,opentracing-go提供基于 Go 的 'context.Context机制 的 helpers 来设置和获取 中的活动Span`。

上一篇 下一篇

猜你喜欢

热点阅读