Tracing卷一:什么是OpenTracing?

2023-09-10 08:11:19 浏览数 (1)

链路追踪

当代互联网服务,通常都是用复杂,大规模分布式集群来实现,微服务化,这些软件模块分布在不同的机器,不同的数据中心,由不同团队,语言开发而成。因此,需要工具帮助理解,分析这些系统、定位问题,做到追踪每一个请求的完整调用链路,收集性能数据,反馈到服务治理中,链路追踪系统应运而生。

现有大部分 APM(Application Performance Management) 理论模型大多借鉴 google dapper 论文,Twitter的Zipkin,Uber的Jaeger,淘宝的鹰眼,大众的CAT,京东的Hydra等。

微服务问题:

  1. 故障定位难
  2. 链路梳理难
  3. 容量预估难

举个例子,一个场景下,一个请求进来,入口服务是 serviceA, serviceA 接到请求后访问数据库读取用户数据,然后向 serviceB 发起 rpc,serviceB 收到 rpc 请求时同时向后端服务 serviceC 和 serviceD 发起请求,等待请求回复后再返回 serviceA 的 rpc 调用。如果我们发现发起的请求失败,或者请求的时延很大,我们该如何去定位呢?

基于这个需求,我们将服务介入追踪系统。

分布式追踪系统发展很快,种类繁多,但核心步骤一般有三个:代码埋点,数据存储、查询展示

在数据采集过程,需要侵入用户代码做埋点,不同系统的API不兼容会导致切换追踪系统需要做很大的改动。为了解决这个问题,诞生了opentracing 规范。

代码语言:javascript复制
    -------------    ---------    ----------    ------------ 
   | Application |  | Library |  |   OSS    |  |  RPC/IPC   |
   |    Code     |  |  Code   |  | Services |  | Frameworks |
    -------------    ---------    ----------    ------------ 
          |              |             |             |
          |              |             |             |
          v              v             v             v
      ----------------------------------------------------- 
     | · · · · · · · · · · OpenTracing · · · · · · · · · · |
      ----------------------------------------------------- 
       |               |                |               |
       |               |                |               |
       v               v                v               v
  -----------    -------------    -------------    ----------- 
 |  Tracing  |  |   Logging   |  |   Metrics   |  |  Tracing  |
 | System A  |  | Framework B |  | Framework C |  | System D  |
  -----------    -------------    -------------    ----------- 

OpenTracing

opentracing (中文)是一套分布式追踪协议,与平台,语言无关,统一接口,方便开发接入不同的分布式追踪系统。

  • 语义规范 : 描述定义的数据模型 Tracer,Sapn 和 SpanContext 等;
  • 语义惯例 : 罗列出 tag 和 logging 操作时,标准的key值;

Trace 和 sapn

在 OpenTracing 中,跟踪信息被分为 Trace和Span 两个部分,它们按照一定的结构存储跟踪信息,所以它们是 OpenTracing 中数据模型的核心。

Trace 是一次完整的跟踪,Trace 由多个 Span 组成。下图是一个 Trace 示例,由 8 个 Span 组成。

代码语言:javascript复制
        [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)

Tracing: a Trace can be thought of as a directed acyclic graph (DAG) of Spans

有点难翻译,大概意思是 Trace 是多个 Span 组成的有向非循环图。

在上面的示例中,一个 Trace 经过了 8 个服务,A -> C -> F -> G 是有严格顺序的,但是从时间上来看,B 、C 是可以并行的。为了准确表示这些 Span 在时间上的关系,我们可以用下图表示:

代码语言:javascript复制
––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–> time

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

有个要注意的地方, 并不是 A -> C -> F 表示 A 执行结束,然后 C 开始执行,而是 A 执行过程中,依赖 C,而 C 依赖 F。因此,当 A 依赖 C 的过程完成后,最终回到 A 继续执行。所以上图中 A 的跨度最大。

Span 格式

要深入学习,就必须先了解 Span对应的json数据: https://github.com/whuanle/DistributedTracing/issues/1

Trace

一个简化的 Trace 如下:

注:不同编程语言的字段名称有所差异,gRPC 和 Restful API 的格式也有所差异。

代码语言:javascript复制
            "traceID": "790e003e22209ca4",
            "spans":[...],
            "processes":{...}

前面说到,在 OpenTracing 中,Trace 是一个有向非循环图,那么 Trace 必定有且只有一个起点。

这个起点会创建一个 Trace 对象,这个对象一开始初始化了 trace id 和 process,trace id 是一个 32 个长度的字符串组成,它是一个时间戳,而 process 是起点进程所在主机的信息。

每个Span封装了如下状态:

  • 操作名称
  • 开始时间戳
  • 结束时间戳
  • 一组零或多个键:值结构的 Span标签 (Tags)。键必须是字符串。值可以是字符串,布尔或数值类型.
  • 一组零或多个 Span日志 (Logs),其中每个都是一个键:值映射并与一个时间戳配对。键必须是字符串,值可以是任何类型。 并非所有的 OpenTracing 实现都必须支持每种值类型。
  • 一个 SpanContext (见下文)
  • 零或多个因果相关的 Span 间的 References (通过那些相关的 Span 的 SpanContext )

每个 SpanContext 封装了如下状态:

  • 任何需要跟跨进程 Span 关联的,依赖于 OpenTracing 实现的状态(例如 Trace 和 Span 的 id)
  • 键:值结构的跨进程的 Baggage Items(区别于 span tag,baggage 是全局范围,在 span 间保持传递,而tag 是 span 内部,不会被子 span 继承使用。)

Inject 和 Extract 操作

既然是分布式的追踪,那肯定涉及到跨进程/机器通讯,在进程间通过传递 Spancontext 来提供足够的信息建立 span 间的关系。

在上游服务中,SpanContext 通过 Inject 操作向 Carrier 中注入tracing信息,传递后在下游服务中通过 Extract 从 Carrier 中提取tracing数据。

关于inject 和 extract

Sampling 采样

OpenTracing API 不强调采样的概念,但是大多数追踪系统通过不同方式实现采样。有些情况下,应用系统需要通知追踪程序,这条特定的调用需要被记录,即使根据默认采样规则,它不需要被记录。sampling.priority tag 提供这样的方式。追踪系统不保证一定采纳这个参数,但是会尽可能的保留这条调用。 sampling.priority - integer

  • 如果大于 0, 追踪系统尽可能保存这条调用链
  • 等于 0, 追踪系统不保存这条调用链
  • 如果此tag没有提供,追踪系统使用自己的默认采样规则

OpenTracing 多语言支持

提供不同语言的 API,用于在自己的应用程序中执行链路记录。

OpenTracing API支持以下平台:

  • Go - https://github.com/opentracing/opentracing-go
  • Python - https://github.com/opentracing/opentracing-python
  • Javascript - https://github.com/opentracing/opentracing-javascript
  • Objective-C - https://github.com/opentracing/opentracing-objc
  • Java - https://github.com/opentracing/opentracing-java
  • C - https://github.com/opentracing/opentracing-cpp

参考资料

https://wu-sheng.gitbooks.io/opentracing-io/content/

https://opentracing-contrib.github.io/opentracing-specification-zh/

https://opentracing.io/specification/

0 人点赞