5

OpenTracing语义规范(中文版)

版本 1.0

原文

The OpenTracing Semantic Specification

概述

这是一份”正式”的OpenTracing语义规范文档。由于OpenTracing是跨语言的,本文档会尽量避免提到特定语言相关的概念。也就是说,我们认为所有语言都具有类似”接口”这样的概念,并提供相关的功能。

版本控制策略

OpenTracing采用了 主版本号.次版本号 形式的版本号,但是没有 .修订号 。当对规范进行向后不兼容的更改时,主版本增加。次版本的增加则用于用于不间断更改,如引入新的标准标记,日志字段或SpanContext引用类型。(你可以在这个Issue specification#2 中读到更多有关此版本控制方案的信息)

OpenTracing数据模型

OpenTracing中的Traces由其Span隐式定义。Trace可被认为是由一组Span定义的有向无环图(DAG),在Span之间的被称为References

以下是一个由8个Span构成的Trace的例子:

一个Trace中Span间的因果关系


        [Span A]  ←←←(the root span)
            |
     +------+------+
     |             |
 [Span B]      [Span C] ←←←(Span C之于Span A的关系是 `ChildOf` )
     |             |
 [Span D]      +---+-------+
               |           |
           [Span E]    [Span F] >>> [Span G] >>> [Span H]
                                       ↑
                                       ↑
                                       ↑
                         (Span G `FollowsFrom` Span F)

有时用时间轴来显示Traces更容易,如下图所示:

一个Trace中Span间的时间关系


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

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

每个Span封装了如下状态:

  • 操作名称

  • 开始时间戳

  • 结束时间戳

  • 一组零或多个键:值结构的Span标签(Tags)。键必须是字符串。值可以是字符串,布尔或数值类型.

  • 一组零或多个Span日志(Logs),其中每个都是一个键:值映射并与一个时间戳配对。键必须是字符串,值可以是任何类型。 并非所有的OpenTracing实现都必须支持每种值类型。

  • 一个SpanContext(见下文)

  • 零或多个因果相关的Span间的References (通过那些相关的SpanSpanContext)

每个SpanContext封装了如下状态:

  • 任何需要跟跨进程Span关联的,依赖于OpenTracing实现的状态(例如Trace和Span的id)

  • 键:值结构的跨进程的Baggage Items

Span间的Reference

一个Span可引用零或多个因果相关的SpanContext。OpenTracing目前定义了两种类型的Reference: ChildOfFollowsFrom这两种Reference类型都对父子Span间的因果关系进行了建模。未来,OpenTracing可能会为不具因果关系的Span提供不同类型的Reference (例如批量的Span,卡在队列中的Span等)。

ChildOf reference: 一个Span可以是另一个Span的子Span。在 ChildOf 引用中,父Span在某种程度上取决于子Span。下列情况会构成 ChildOf 关系:

  • 在一个RPC中,代表服务端的Span可作为 ChildOf 代表客户端的Span

  • 在一个持久化进程中,代表SQL插入的Span可作为 ChildOf 代表ORM save方法的Span

  • 多个并发(可能是分布式)执行任务的Span可能分别各自为 Childof 一个合并了多个子Span结果的父Span

下列这些都是有效的具有 ChildOf 关系的时序图

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

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

FollowsFrom reference: 有些父Span不依赖于任何子Span的结果。这种情况下,我们仅认为子Span在因果上 FollowsFrom 父Span。有许多不同的 FollowsFrom 引用子类别,在OpenTracing的未来版本中,它们可能会被更正式地区分。

下列这些都是有效的具有 FollowsFrom 关系的时序图

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


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


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

OpenTracing接口定义

OpenTracing规范中有三种相互关联的关键类型 TracerSpan ,and SpanContext。接下来,我们来看一下各种类型的行为; 简单来说,每种行为分别在编程语言中对应为一个”方法”,尽管实际上可能是一组重载方法。

当我们讨论“可选”参数时,应当清楚,不同的语言有不同的方式来解释这个概念。比如说,在Go语言中我们会使用”functional Options”这个术语,而在Java中我们会使用builder模式。

Tracer

Tracer 接口创造 Span 并且能够跨进程地 Inject (序列化)和 Extract (反序列化)。严格来说,它应具有以下能力:

启动一个新的 Span

必须参数

  • 操作名称,一个人工可读的字符串,它简洁地表示由Span完成的工作 (例如,RPC方法名称、函数名称或一个较大的计算任务中的阶段的名称)。操作名称应该用泛化的字符串形式标识出一个Span实例. 也就是说,get_userget_user/314159 好。

比如说这里有几个操作名称,用于得到一个虚构的账户信息:

操作名称 建议
get 太宽泛
get_account/792 太具体
get_account 刚刚好,account_id=792 可作为一个合适的 Span 标签

可选参数

  • 零或多个与 SpanContext 相关的references,尽可能包括 ChildOfFollowFrom 引用类型的信息

  • 开始时间戳,如果没有的话,默认使用现实时间(walltime)

  • 零或多个标签

返回值是一个刚刚启动的 Span 实例

注入(Inject) SpanContext到载体中

必须参数

  • SpanContext实例

  • 格式描述符(format descriptor)(通常但不一定是字符串常量),告诉Tracer的实现如何在载体对象中对SpanContext进行编码

  • 载体(carrier),其类型由格式描述符指定。Tracer的实现将根据格式描述对此载体对象中的SpanContext进行编码

从载体中抽取(Extract) SpanContext

必须参数

  • 格式描述符(format descriptor),告诉Tracer的实现如何在载体对象中对SpanContext进行解码

  • 载体,其类型由格式描述符指定。 Tracer的实现将根据格式描述对此载体对象中的SpanContext进行解码

返回值是一个SpanContext实例,适合作为通过Tracer启动Span时的reference

注意: Injection和Extraction所需的格式

Injection和Extraction都依赖于一个可扩展的格式描述符参数,其指定了相关联的”载体”类型以及 SpanContext 是如何被编码的。Tracer实现必须支持下列所有格式。

  • 文本映射(Text Map): 任意的无字符编码限制的string-string型键值结构

  • HTTP Headers: string-string型的键值结构在HTTP headers( RFC 7230 )中也适用。在实操中,由于HTTP header很容易被各种不同的方式处理, 强烈建议在Tracer的实现中使用有限的键空间和保守的转意值

  • 二进制: 代表了 SpanContext 的任意二进制数据

Span

除了获取 SpanSpanContext 的方法之外,下面任何方法都不能在 Span 完成后被调用。

获取 SpanSpanContext

无需参数。

返回值是 SpanSpanContext 。返回值甚至可能在 Span 结束后被使用。

重写操作名

必须参数

  • 新的操作名,当 Span 启动时取代旧内容

结束 Span

可选参数

  • 显式地添加Span的结束时间戳,如果没有这个参数则会隐式的添加现实时间(walltime)
    除了获取Span的SpanContext的方法之外,任何Span对象的方法都不能在其完成后被调用。

设置 Span 标签

必须参数

  • 标签的键,必须是字符串

  • 标签的值,必须是字符串,布尔值,数值类型其中之一

注意OpenTracing项目文档中已经为一些”标准标签”规定了语义。

记录(Log) 结构化的数据

必须参数

  • 一到多个键值对,键必须是字符串,值可以是任意类型。有些OpenTracing的实现可以处理更多的日志的值。

可选参数

  • 显式时间戳。该参数必须落在当前Span的开始与结束时间戳之间。

注意OpenTracing项目文档中已经为一些“日志的标准键“ 规定了语义。

设置(set) baggage item

Baggage item是对应于某个 Span 及其 SpanContext ,以及所有直接或间接引用自本地(local) SpanSpan 的键值对。也就是说,baggage items是与其trace一起传播的。

Baggage item为全栈式的OpenTracing集成提供了强大的功能(比如在移动App上使用时,它可以一路追踪数据直至储存系统的深度),不过使用这些功能时要当心。

每个键值都会被拷贝到每一个本地(local)及远程的子Span,这可能导致巨大的网络和CPU开销。

必须参数

  • baggage键,字符串

  • baggage值,字符串

获取(get) baggage item

必须参数

  • baggage键,字符串

返回值是对应的baggage值,或是一个表示没有baggage值的一个量。

SpanContext

在一般所指的OpenTracing层面上,SpanContext 更像是一个概念而不是一种实用功能。也就是说,这对OpenTracing的实现来说提供一层薄的接口是至关重要的。当启动一个 Span 或者注入/提取(injecting/extracting) trace时,多数OpenTracing用户仅通过referenceSpanContext 进行交互。

在OpenTracing中,SpanContext 被强制设定为不可变的(immutable),以应对在 Span 结束时和引用(reference)时产生的复杂的生命周期问题。

遍历所有的baggage item

不同语言对此有不同的建模方式,不过给出一个 SpanContext 实例,语义上还是应该能让调用者在短时间内遍历baggage items。

NoopTracer

所有语言的OpenTracing API必须提供某种 NoopTracer 实现,可用作标记控制(flag-control)或者进行一些用于测试的无害注入等。某些情况下(比如Java),NoopTracer 可能在它自己的制品包(packaging artifact)中。

可选的API元素

有些语言提供了在单进程中用来传递活动的 SpanSpanContext 的工具。比如,opentracing-go 提供了在Go的 context.Context 机制中,用来set和get活动 Span 的帮助函数(helpers)。


sfpprxy
95 声望4 粉丝