您现在的位置是:网站首页> 编程资料编程资料

Jaeger Client Go入门并实现链路追踪_Golang_

2023-05-26 416人已围观

简介 Jaeger Client Go入门并实现链路追踪_Golang_

Jaeger

OpenTracing 是开放式分布式追踪规范,OpenTracing API 是一致,可表达,与供应商无关的API,用于分布式跟踪和上下文传播。

OpenTracing 的客户端库以及规范,可以到 Github 中查看:https://github.com/opentracing/

Jaeger 是 Uber 开源的分布式跟踪系统,详细的介绍可以自行查阅资料。

部署 Jaeger

这里我们需要部署一个 Jaeger 实例,以供微服务以及后面学习需要。

使用 Docker 部署很简单,只需要执行下面一条命令即可:

docker run -d -p 5775:5775/udp -p 16686:16686 -p 14250:14250 -p 14268:14268 jaegertracing/all-in-one:latest

访问 16686 端口,即可看到 UI 界面。

后面我们生成的链路追踪信息会推送到此服务,而且可以通过 Jaeger UI 查询这些追踪信息。

从示例了解 Jaeger Client Go

这里,我们主要了解一些 Jaeger Client 的接口和结构体,了解一些代码的使用。

为了让读者方便了解 Trace、Span 等,可以看一下这个 Json 的大概结构:

 { "traceID": "2da97aa33839442e", "spans": [ { "traceID": "2da97aa33839442e", "spanID": "ccb83780e27f016c", "flags": 1, "operationName": "format-string", "references": [...], "tags": [...], "logs": [...], "processID": "p1", "warnings": null }, ... ... ], "processes": { "p1": { "serviceName": "hello-world", "tags": [...] }, "p2": ..., "warnings": null } 

创建一个 client1 的项目,然后引入 Jaeger client 包。

go get -u github.com/uber/jaeger-client-go/

然后引入包

import ( "github.com/uber/jaeger-client-go" )

了解 trace、span

链路追踪中的一个进程使用一个 trace 实例标识,每个服务或函数使用一个 span 标识,jaeger 包中有个函数可以创建空的 trace:

tracer := opentracing.GlobalTracer() // 生产中不要使用

然后就是调用链中,生成父子关系的 Span:

func main() { tracer := opentracing.GlobalTracer() // 创建第一个 span A parentSpan := tracer.StartSpan("A") defer parentSpan.Finish() // 可手动调用 Finish() } func B(tracer opentracing.Tracer,parentSpan opentracing.Span){ // 继承上下文关系,创建子 span childSpan := tracer.StartSpan( "B", opentracing.ChildOf(parentSpan.Context()), ) defer childSpan.Finish() // 可手动调用 Finish() }

每个 span 表示调用链中的一个结点,每个结点都需要明确父 span。

现在,我们知道了,如何生成 trace{span1,span2},且 span1 -> span2 即 span1 调用 span2,或 span1 依赖于 span2。

tracer 配置

由于服务之间的调用是跨进程的,每个进程都有一些特点的标记,为了标识这些进程,我们需要在上下文间、span 携带一些信息。

例如,我们在发起请求的第一个进程中,配置 trace,配置服务名称等。

// 引入 jaegercfg "github.com/uber/jaeger-client-go/config" cfg := jaegercfg.Configuration{ ServiceName: "client test", // 对其发起请求的的调用链,叫什么服务 Sampler: &jaegercfg.SamplerConfig{ Type: jaeger.SamplerTypeConst, Param: 1, }, Reporter: &jaegercfg.ReporterConfig{ LogSpans: true, }, }

Sampler 是客户端采样率配置,可以通过 sampler.type 和 sampler.param 属性选择采样类型,后面详细聊一下。

Reporter 可以配置如何上报,后面独立小节聊一下这个配置。

传递上下文的时候,我们可以打印一些日志:

 jLogger := jaegerlog.StdLogger

配置完毕后就可以创建 tracer 对象了:

 tracer, closer, err := cfg.NewTracer( jaegercfg.Logger(jLogger), ) defer closer.Close() if err != nil { }

完整代码如下:

import ( "github.com/opentracing/opentracing-go" "github.com/uber/jaeger-client-go" jaegercfg "github.com/uber/jaeger-client-go/config" jaegerlog "github.com/uber/jaeger-client-go/log" ) func main() { cfg := jaegercfg.Configuration{ ServiceName: "client test", // 对其发起请求的的调用链,叫什么服务 Sampler: &jaegercfg.SamplerConfig{ Type: jaeger.SamplerTypeConst, Param: 1, }, Reporter: &jaegercfg.ReporterConfig{ LogSpans: true, }, } jLogger := jaegerlog.StdLogger tracer, closer, err := cfg.NewTracer( jaegercfg.Logger(jLogger), ) defer closer.Close() if err != nil { } // 创建第一个 span A parentSpan := tracer.StartSpan("A") defer parentSpan.Finish() B(tracer,parentSpan) } func B(tracer opentracing.Tracer, parentSpan opentracing.Span) { // 继承上下文关系,创建子 span childSpan := tracer.StartSpan( "B", opentracing.ChildOf(parentSpan.Context()), ) defer childSpan.Finish() }

启动后:

2021/03/30 11:14:38 Initializing logging reporter 2021/03/30 11:14:38 Reporting span 689df7e83255d05d:75668e8ed5ec61da:689df7e83255d05d:1 2021/03/30 11:14:38 Reporting span 689df7e83255d05d:689df7e83255d05d:0000000000000000:1 2021/03/30 11:14:38 DEBUG: closing tracer 2021/03/30 11:14:38 DEBUG: closing reporter

Sampler 配置

sampler 配置代码示例:

 Sampler: &jaegercfg.SamplerConfig{ Type: jaeger.SamplerTypeConst, Param: 1, }

这个 sampler 可以使用 jaegercfg.SamplerConfig,通过 typeparam 两个字段来配置采样器。

为什么要配置采样器?因为服务中的请求千千万万,如果每个请求都要记录追踪信息并发送到 Jaeger 后端,那么面对高并发时,记录链路追踪以及推送追踪信息消耗的性能就不可忽视,会对系统带来较大的影响。当我们配置 sampler 后,jaeger 会根据当前配置的采样策略做出采样行为。

详细可以参考:https://www.jaegertracing.io/docs/1.22/sampling/

jaegercfg.SamplerConfig 结构体中的字段 Param 是设置采样率或速率,要根据 Type 而定。

下面对其关系进行说明:

TypeParam说明
"const"0或1采样器始终对所有 tracer 做出相同的决定;要么全部采样,要么全部不采样
"probabilistic"0.0~1.0采样器做出随机采样决策,Param 为采样概率
"ratelimiting"N采样器一定的恒定速率对tracer进行采样,Param=2.0,则限制每秒采集2条
"remote"采样器请咨询Jaeger代理以获取在当前服务中使用的适当采样策略。

sampler.Type="remote"/sampler.Type=jaeger.SamplerTypeRemote 是采样器的默认值,当我们不做配置时,会从 Jaeger 后端中央配置甚至动态地控制服务中的采样策略。

Reporter 配置

看一下 ReporterConfig 的定义。

type ReporterConfig struct { QueueSize int `yaml:"queueSize"` BufferFlushInterval time.Duration LogSpans bool `yaml:"logSpans"` LocalAgentHostPort string `yaml:"localAgentHostPort"` DisableAttemptReconnecting bool `yaml:"disableAttemptReconnecting"` AttemptReconnectInterval time.Duration CollectorEndpoint string `yaml:"collectorEndpoint"` User string `yaml:"user"` Password string `yaml:"password"` HTTPHeaders map[string]string `yaml:"http_headers"` }

Reporter 配置客户端如何上报追踪信息的,所有字段都是可选的。

这里我们介绍几个常用的配置字段。

  • QUEUESIZE,设置队列大小,存储采样的 span 信息,队列满了后一次性发送到 jaeger 后端;defaultQueueSize 默认为 100;

  • BufferFlushInterval 强制清空、推送队列时间,对于流量不高的程序,队列可能长时间不能满,那么设置这个时间,超时可以自动推送一次。对于高并发的情况,一般队列很快就会满的,满了后也会自动推送。默认为1秒。

  • LogSpans 是否把 Log 也推送,span 中可以携带一些日志信息。

  • LocalAgentHostPort 要推送到的 Jaeger agent,默认端口 6831,是 Jaeger 接收压缩格式的 thrift 协议的数据端口。

  • CollectorEndpoint 要推送到的 Jaeger Collector,用 Collector 就不用 agent 了。

例如通过 http 上传 trace:

 Reporter: &jaegercfg.ReporterConfig{ LogSpans: true, CollectorEndpoint: "http://127.0.0.1:14268/api/traces", },

据黑洞大佬的提示,HTTP 走的就是 thrift,而 gRPC 是 .NET 特供,所以 reporter 格式只有一种,而且填写 CollectorEndpoint,我们注意要填写完整的信息。

完整代码测试:

import ( "bufio" "github.com/opentracing/opentracing-go" "github.com/uber/jaeger-client-go" jaegercfg "github.com/uber/jaeger-client-go/config" jaegerlog "github.com/uber/jaeger-client-go/log" "os" ) func main() { var cfg = jaegercfg.Configuration{ ServiceName: "client test", // 对其发起请求的的调用链,叫什么服务 Sampler: &jaegercfg.SamplerConfig{ Type: jaeger.SamplerTypeConst, Param: 1, }, Reporter: &jaegercfg.ReporterConfig{ LogSpans: true, CollectorEndpoint: "http://127.0.0.1:14268/api/traces", }, } jLogger := jaegerlog.StdLogger tracer, closer, _ := cfg.NewTracer( jaegercfg.Logger(jLogger), ) // 创建第一个 span A parentSpan := tracer.StartSpan("A") // 调用其它服务 B(tracer, parentSpan) // 结束 A parentSpan.Finish() // 结束当前 tracer closer.Close() reader := bufio.NewReader(os.Stdin) _, _ = reader.ReadByte() } func B(tracer opentracing.Tracer, parentSpan opentracing.Span) { // 继承上下文关系,创建子 span childSpan := tracer.StartSpan( "B", opentracing.ChildOf(parentSpan.Context()), ) defer childSpan.Finish() }

运行后输出结果:

2021/03/30 15:04:15 Initializing logging reporter 2021/03/30 15:04:15 Reporting span 715e0af47c7d9acb:7dc9a6b568951e4f:715e0af47c7d9acb:1 2021/03/30 15:04:15 Reporting span 715e0af47c7d9acb:715e0af47c7d9acb:0000000000000000:1 2021/03/30 15:04:15 DEBUG: closing tracer 2021/03/30 15:04:15 DEBUG: closing reporter 2021/03/30 15:04:15 DEBUG: flushed 1 spans 2021/03/30 15:04:15 DEBUG: flushed 1 spans

打开 Jaeger UI,可以看到已经推送完毕(http://127.0.0.1:16686)。

上传的trace

这时,我们可以抽象代码代码示例:

提示: 本文由整理自网络,如有侵权请联系本站删除!
本站声明:
1、本站所有资源均来源于互联网,不保证100%完整、不提供任何技术支持;
2、本站所发布的文章以及附件仅限用于学习和研究目的;不得将用于商业或者非法用途;否则由此产生的法律后果,本站概不负责!

-六神源码网