全部产品
云市场

基本概念

更新时间:2018-09-18 21:38:24

为什么需要分布式追踪系统?

为了应对各种复杂的业务,开发工程师开始采用敏捷开发、持续集成等开发方式。系统架构也从单机大型软件演化成微服务架构。微服务构建在不同的软件集上,这些软件模块可能是由不同团队开发的,可能使用不同的编程语言来实现,还可能发布在多台服务器上。因此,如果一个服务出现问题,可能导致几十个应用都出现服务异常。

分布式追踪系统可以记录请求范围内的信息,例如一次远程方法调用的执行过程和耗时,是我们排查系统问题和系统性能的重要工具。

什么是调用链(Trace)?

在广义上,一个调用链代表一个事务或者流程在(分布式)系统中的执行过程。在 OpenTracing 标准中,调用链是多个 Span 组成的一个有向无环图(Directed Acyclic Graph,简称 DAG),每一个 Span 代表调用链中被命名并计时的连续性执行片段。

下图是一个分布式调用的例子:客户端发起请求,请求首先到达负载均衡器,接着经过认证服务、计费服务,然后请求资源,最后返回结果。

数据被采集存储后,分布式追踪系统一般会选择使用包含时间轴的时序图来呈现这个调用链。

OpenTracing 数据模型

整体概念

OpenTracing 中的调用链(Trace)通过归属于此调用链的 Span 来隐性地定义。一条调用链可以视为一个由多个 Span 组成的有向无环图(DAG 图)。Span 之间的关系被命名为 References。例如下面的示例调用链就是由 8 个 Span 组成的。

  1. 单个 Trace Span 间的因果关系
  2. [Span A] ←←←(The root span)
  3. |
  4. +------+------+
  5. | |
  6. [Span B] [Span C] ←←←(Span C Span A 的子节点,ChildOf)
  7. | |
  8. [Span D] +---+-------+
  9. | |
  10. [Span E] [Span F] >>> [Span G] >>> [Span H]
  11. Span G Span F 后被调用, FollowsFrom

有些情况下,使用下面这种基于时间轴的时序图可以更好地展现调用链。

  1. 单个 Trace Span 间的时间关系
  2. ––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–> time
  3. [Span A···················································]
  4. [Span B··············································]
  5. [Span D··········································]
  6. [Span C········································]
  7. [Span E·······] [Span F··] [Span G··] [Span H··]

链路

Tracer 接口用于创建 Span(startSpan 函数)、解析上下文(Extract 函数)和透传上下文(Inject 函数)。它具有以下能力:

  • 创建一个新 Span 或者设置 Span 属性

    1. /** 创建和开始一个span,返回一个span,span中包括操作名称和设置的选项。
    2. ** 例如:
    3. ** 创建一个无parentSpan的Span:
    4. ** sp := tracer.StartSpan("GetFeed")
    5. ** 创建一个有parentSpan的Span
    6. ** sp := tracer.StartSpan("GetFeed",opentracing.ChildOf(parentSpan.Context()))
    7. **/
    8. StartSpan(operationName string, opts ...StartSpanOption) Span

    每个 Span 包含以下对象:

    • Operation name:操作名称 (也可以称作 Span name)。
    • Start timestamp:起始时间。
    • Finish timestamp:结束时间。
    • Span tag:一组键值对构成的 Span 标签集合。键值对中,键必须为 String,值可以是字符串、布尔或者数字类型。
    • Span log:一组 Span 的日志集合。每次 Log 操作包含一个键值对和一个时间戳。键值对中,键必须为 String,值可以是任意类型。
    • SpanContext: pan 上下文对象。每个 SpanContext 包含以下状态:
      • 要实现任何一个 OpenTracing,都需要依赖一个独特的 Span 去跨进程边界传输当前调用链的状态(例如:Trace 和 Span 的 ID)。
      • Baggage Items 是 Trace 的随行数据,是一个键值对集合,存在于 Trace 中,也需要跨进程边界传输。
    • References(Span 间关系):相关的零个或者多个 Span(Span 间通过 SpanContext 建立这种关系)。
  • 透传数据

    透传数据分为两步:

    1. 从请求中解析出 SpanContext。

      1. // Inject() takes the `sm` SpanContext instance and injects it for
      2. // propagation within `carrier`. The actual type of `carrier` depends on
      3. // the value of `format`.
      4. /** 根据format参数从请求(Carrier)中解析出SpanContext(包括traceId、spanId、baggage)。
      5. ** 例如:
      6. ** carrier := opentracing.HTTPHeadersCarrier(httpReq.Header)
      7. ** clientContext, err := tracer.Extract(opentracing.HTTPHeaders, carrier)
      8. **/
      9. Extract(format interface{}, carrier interface{}) (SpanContext, error)
    2. 将 SpanContext 注入到请求中。

      1. /**
      2. ** 将SpanContext中的traceId,spanId,Baggage等根据format参数注入到请求中(Carrier),
      3. ** e.g
      4. ** carrier := opentracing.HTTPHeadersCarrier(httpReq.Header)
      5. ** err := tracer.Inject(span.Context(), opentracing.HTTPHeaders, carrier)
      6. **/
      7. Inject(sm SpanContext, format interface{}, carrier interface{}) error

数据是如何上报的?

不通过 Agent 而直接上报数据的原理如下图所示。

通过 Agent 上报数据的原理如下图所示。