Go使用Jaeger实现分布式链路追踪

了解分布式链路追踪

什么是分布式链路追踪?

  • 分布式链路追踪式用来查看和了解复杂的微服务间交互中的整个活动链
  • 现代的云原生软件开发十分依赖微服务,因为每个独立的服务提供不同的核心功能。当用户在应用中发出请求时,许多单独的服务器都会作出响应,产生相应的结果
  • 应用中的一个调用可能会涉及几十项彼此交互的服务。当出现问题时,开发人员和工程师该如何查明问题所在?所以需要一种跟踪所有连接的方法,就是分布式链路追踪

什么是OpenTracing?

OpenTracing是CNCF托管的分布式追踪项目,官方定位是针对分布式系统的追踪的API标准库,旨在为不同的分布式追踪系统提供统一的对外API接入层。它位于应用程序/类库追踪或日志分析程序之间。

OpenTracing 数据模型

  • Trace 事物在分布式系统中移动时的描述

  • Span 一种命名的、定时的操作,表示工作流的一部分,span中包含一下状态

    • Span Tag,一组键值对构成的 Span 标签集合。键值对中,键必须为 string,值可以是字符串,布尔,或者数字类型。
    • Span Log,一组 span 的日志集合。每次 log 操作包含一个键值对,以及一个时间戳。
  • Span Contenxt 携带分布式事务的跟踪信息,包括当它通过网络或消息总线将服务传递给服务时。Span上下文包含Trace标识符、Span标识符和跟踪系统需要传播到下游服务的任何其他数据

特别说明,一条 Trace(调用链)可以被认为是一个由多个 Span 组成的有向无环图(DAG图),SpanSpan 的关系被命名为 References。Trace调用链可以用树形结构或者基于时间轴的时序图表示。下图为树形结构表示调用关系:

image-20211202102756151

有时,使用时间轴来可视化Trace会更容易,如下图

image-20211202102729427

什么是Jaeger?

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

Jaeger是Uber推出的一款开源分布式追踪系统,兼容OpenTracing API,它用于监视和诊断基于微服务的分布式系统,分布式追踪系统用于记录请求范围内的信息,例如:一次远程方法调用的执行过程和耗时。是我们排查问题和系统性能的利器

Jaeger的优点

  • 兼容OpenTracing API,写起来方便简单
  • UI相较于Zipkin更加直观和丰富
  • sdk比较丰富,go语言编写,支持Go、Java、Python、C++、C#、Node
  • 上传采用udp传输,效率高速度快
  • 后台存储支持Cassandraeskafka

Jaeger架构图

image-20211202105921391

Jaeger组件介绍

  • jaeger-clientjaeger 的客户端,实现了opentracing协议 开销很小

  • jaeger-agentjaeger client的一个代理程序,client将收集到的调用链数据发给agent,然后由agent发给collector

  • jaeger-collector:负责接收jaeger client或者jaeger agent上报上来的调用链数据,然后做一些校验,比如时间范围是否合法等,最终会经过内部的处理存储到后端存储

  • jaeger-query:专门负责调用链查询的一个服务,有自己独立的UI

  • jaeger-ingester:中文名称“摄食者”,可用从kafka读取数据然后写到jaeger的后端存储,比如CassandraElasticsearch

其中jaeger-collectorjaeger-query是必须的,其余的都是可选的,我们没有采用agent上报的方式,而是让客户端直接通过endpoint上报到collector

使用Jaeger

引用相关库

1
2
go get -u github.com/opentracing/opentracing-go
go get -u github.com/uber/jaeger-client-go

使用Jaeger

初始化jaeger

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var (
tracer opentracing.Tracer
closer io.Closer
)

func initJaeger(serviceName string){
cfg := &config.Configuration{
Sampler: &config.SamplerConfig{ //采样类型设置
Type: "const",
Param: 1,
},
Reporter: &config.ReporterConfig{ // 采样接口设置
LogSpans: true,
LocalAgentHostPort:"xxx:6831",
},
}
cfg.ServiceName = serviceName // 采集服务器名字 是必要的

tracer, closer, _ = cfg.NewTracer( // 生成jaeger tracer
config.Logger(jaeger.StdLogger), // 每次调用时打印日志
)

opentracing.SetGlobalTracer(tracer) // 生成全局单例tracer
}

其中SamplerConfig用于设置采样类型,Type分为:

  • const:全量采集。param采样率设置0,1 分别对应打开和关闭
  • probabilistic :概率采集。param默认万份之一,0~1之间取值,
  • rateLimiting :限速采集。param每秒采样的个数
  • remote :动态采集策略。param值与probabilistic的参数一样。在收到实际值之前的初始采样率。改值可以通过环境变量的JAEGER_SAMPLER_PARAM设定
1
2
3
4
5
6
7
8
9
记录关于Span相关信息
//method 1、Adds a tag to the span.
SetTag(key string, value interface{}) Span

//method 2、有类型检查、但相较LogKV比较麻烦
LogFields(fields ...log.Field)

//method 3、缺乏类型检查
LogKV(alternatingKeyValues ...interface{})

在最初调用时(放在中间件等)生成ctx,并将span注入进去

1
2
span, ctx := opentracing.StartSpanFromContext(ctx, name) // 开始生成子span
defer span.Finish()

通过ctx拿到span,可以将kv注入进去

1
2
3
4
5
func setSpanLog(ctx context.Context,msg string,level string){
span := opentracing.SpanFromContext(ctx) // 获取ctx中的span
span.LogFields(log.String("event", msg)) // 注册kv键值对
span.LogFields(log.String("level", level))
}

将jaeger集成到micro中

micro插件中提供了4种wrapper,分别用于不同服务类型: WrapHandler( ) server中间件 、WrapCall( ) call中间件 、WrapClient( ) client中间件 、 WrapSubscriber( ) 订阅中间件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
srv := micro.NewService(
micro.WrapClient(
opentracing.NewClientWrapper(trace.Tracer()),
logger.LogClient,
),
micro.WrapHandler(
opentracing.NewHandlerWrapper(trace.Tracer()),
logger.LogHandler,
),
micro.WrapSubscriber(
opentracing.NewSubscriberWrapper(trace.Tracer()),
),
micro.WrapCall(
opentracing.NewCallWrapper(trace.Tracer()),
),
)

测试部署

快速搭建环境

1
2
docker run -d -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 -p5775:5775/udp -p6831:6831/udp -p6832:6832/udp \
-p5778:5778 -p16686:16686 -p14268:14268 -p9411:9411 jaegertracing/all-in-one:latest

端口 协议 组件 功能
5775 UDP协议 agent 接受zipkin.thrift紧凑型节俭协议(不推荐使用,仅由旧版客户端使用)
6831 UDP协议 agent 接受jaeger.thrift紧凑型节俭协议
6832 UDP协议 agent jaeger.thrift通过二进制节俭协议接受
5778 HTTP agent 服务配置
16686 HTTP query 服务前端
14268 HTTP collector jaeger.thrift直接接受客户
14250 HTTP collector 接受 model.proto
9411 HTTP collector Zipkin兼容端点(可选)

成功启动后,可以通过http://xxx:16686访问 Jaeger-UI

image-20211202141018674


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!