1

链路追踪

分布式追踪论文

论文地址:http://bigbully.github.io/Dapper-translation/ 11

Opentracing协议

协议标准

中文文档:

https://zhuanlan.zhihu.com/p/...

https://github.com/opentracin...

OpenTracing中的Trace(调用链)通过归属于此调用链的Span来隐性的定义。 特别说明,一条Trace(调用链)可以被认为是一个由多个Span组成的有向无环图(DAG图), SpanSpan的关系被命名为References

Span状态:

  • SpanContext,Span上下文对象

Span间状态:

  • ChildOf(父子) :一个span可能是一个父级span的孩子,即"ChildOf"关系。
  • FollowsFrom(跟随):一些父级节点不以任何方式依赖他们子节点的执行结果

API

  • Tracer接口用来创建Span,以及处理如何处理Inject(serialize) 和 Extract (deserialize),用于跨进程边界传递。
  • Span结束后(span.finish()),除了通过Span获取SpanContext外,下列其他所有方法都不允许被调用。

协议作用

  • OpenTracing通过提供平台无关、厂商无关的API,使得开发人员能够方便的添加(或更换)追踪系统的实现。
  • 可以很自由的在不同的分布式追踪系统中切换
  • 不负责具体实现

主要组成

  • Trace:一个Trace代表一个事务或者流程在(分布式)系统中的执行过程
  • Span:记录Trace在执行过程中的信息
  • 无限极分类:服务与服务之间使用无限极分类的方式,通过HTTP头部或者请求地址传输到最低层,从而把整个调用链串起来。

数据模型

OpenTracing中的Trace(调用链)通过归属于此调用链的Span来隐性的定义。 特别说明,一条Trace(调用链)可以被认为是一个由多个Span组成的有向无环图(DAG图), SpanSpan的关系被命名为References

Span状态:

  • SpanContext,Span上下文对象

Span间状态:

  • ChildOf(父子) :一个span可能是一个父级span的孩子,即"ChildOf"关系。
  • FollowsFrom(跟随):一些父级节点不以任何方式依赖他们子节点的执行结果

API

  • Tracer接口用来创建Span,以及处理如何处理Inject(serialize) 和 Extract (deserialize),用于跨进程边界传递。
  • Span结束后(span.finish()),除了通过Span获取SpanContext外,下列其他所有方法都不允许被调用。

常见链路追踪软件

Jaeger,Zipkin,Skywalking

Jaeger

官网:https://www.jaegertracing.io/

结构

Agent,Collector,Query,Ingester

Architecture

安装

docker-compose.yaml

version: "3"

services:
  jaeger: 
    image: jaegertracing/all-in-one:1.32
    ports:
      - "5775:5775/udp"
      - "6831:6831/udp"
      - "6832:6832/udp"
      - "5778:5778"
      - "16686:16686"
      - "14250:14250"
      - "14268:14268"
      - "14269:14269"
      - "9411:9411"

运行

docker-compose up -d

访问

http://192.168.31.51:16686

Go集成Jaeger

基础使用

micro/cartorder/jaeger/main.go

package main

import (
    "time"

    "github.com/uber/jaeger-client-go"
    jaegercfg "github.com/uber/jaeger-client-go/config"
)

func main() {
    cfg := jaegercfg.Configuration{
        Sampler: &jaegercfg.SamplerConfig{
            Type:  jaeger.SamplerTypeConst,
            Param: 1,
        },
        Reporter: &jaegercfg.ReporterConfig{
            LogSpans:           true,
            LocalAgentHostPort: "192.168.31.51:6831",
        },
        ServiceName: "TestServiceName",
    }
    tracer, closer, err := cfg.NewTracer(jaegercfg.Logger(jaeger.StdLogger))
    defer closer.Close()
    if err != nil {
        panic(err)
    }
    span := tracer.StartSpan("account_web")
    defer span.Finish()
    time.Sleep(1 * time.Second)
}
嵌套
parentSpan := tracer.StartSpan("order_web")

span1 := tracer.StartSpan("cart_srv", opentracing.ChildOf(parentSpan.Context()))
time.Sleep(1 * time.Second)
span1.Finish()

span2 := tracer.StartSpan("product_srv", opentracing.ChildOf(parentSpan.Context()))
time.Sleep(2 * time.Second)
span2.Finish()

span3 := tracer.StartSpan("stock_srv", opentracing.ChildOf(parentSpan.Context()))
time.Sleep(3 * time.Second)
span3.Finish()

parentSpan.Finish()

Jaeger-client实现

  • 提取 Extrace

    • 为什么要提取:找到父亲
    • 从哪里提取:进程内,不同进程之间各自约定
    • 提取什么:

      • traceid
      • spanid
      • parentid
      • 是否采集
  • 注入 Inject

    • 为什么要注入:为了让孩子能找到爸爸
    • 注入到哪里:和提取相对
    • 注入了什么:和提取相对
  • 异步report

    • Span.finish
    • 把Span放入队列
    • 从队列取出,生成thrift,放入spanBuffer
    • Flush到远程

低消耗

  • 消耗在哪里:Jaeger-client作用于应用层,提取、注入、生成span、序列化成Thrift、发送到远程等,一系列操作这些都会带来性能上的损耗。
  • 采集策略

    • Constant
    • Probabilistic 概率随机
    • Rate Limiting
    • Remote

应用透明

  • 如何做到让业务开发人员无感知

    • Golang:约定第一个参数为ctx,把parentSpan放入ctx
    • PHP:使用全局变量

服务端实现

  • Jaeger-agent负责上报数据的整理
  • Jaeger-collector负责数据保存
  • Jaeger-query负责数据查询
  • Jaeger-agent和Jaeger-collector使用基于TCP协议实现的RPC进行通讯

Jaeger-agent

  • 监听3个UDP端口
  • 接收Jaeger-client的数据,放入队列dataChan
  • 从队列dataChan获取数据,进行校验
  • 提交数据

Jaeger-collector 源码阅读

  • 协程池:启动时划分固定数量的协程
  • 接收jaeger-agent数据
  • 放入队列
  • 从队列拿出来,写入数据库

Reference

第 29 期 2019-01-23 Go opentracing jaeger 集成及源码分析


IT小马
1.2k 声望166 粉丝

Php - Go - Vue - 云原生