[系列] go-gin-api 路由中间件 - Jaeger 链路追踪(六)
程序员文章站
2022-05-04 12:29:54
[TOC] 概述 首先同步下项目概况: 上篇文章分享了,路由中间件 Jaeger 链路追踪(理论篇),这篇文章咱们接着分享:路由中间件 Jaeger 链路追踪(实战篇)。 这篇文章,确实让大家久等了,主要是里面有一些技术点都是刚刚研究的,没有存货。 先看下咱们要实现的东西: API 调用了 5 个服 ......
概述
首先同步下项目概况:
上篇文章分享了,路由中间件 - jaeger 链路追踪(理论篇),这篇文章咱们接着分享:路由中间件 - jaeger 链路追踪(实战篇)。
这篇文章,确实让大家久等了,主要是里面有一些技术点都是刚刚研究的,没有存货。
先看下咱们要实现的东西:
api 调用了 5 个服务,其中 4 个 grpc 服务,1 个 http 服务,服务与服务之间又相互调用:
- speak 服务,又调用了 listen 服务 和 sing 服务。
- read 服务,又调用了 listen 服务 和 sing 服务。
- write 服务,又调用了 listen 服务 和 sing 服务。
咱们要实现的就是查看 api 调用的链路。
关于一些理论的东西,大家可以去看看上篇文章或查阅一些资料,这篇文章就是实现怎么用。
ok,开整。
jaeger 部署
咱们使用 all in one 的方式,进行本地部署。
下载地址:
我的电脑是 macos 选择 -> binaries -> macos
下载后并解压,会发现以下文件:
- example-hotrod
- jaeger-agent
- jaeger-all-in-one
- jaeger-collector
- jaeger-ingester
- jaeger-query
进入到解压后的目录执行:
./jaeger-all-in-one
目测启动后,访问地址:
到这,jaeger 已经部署成功了。
准备测试服务
准备的五个测试服务如下:
听(listen)
- 端口:9901
- 通讯:grpc
说(speak)
- 端口:9902
- 通讯:grpc
读(read)
- 端口:9903
- 通讯:grpc
写(write)
- 端口:9904
- 通讯:grpc
唱(sing)
- 端口:9905
- 通讯:http
听、说、读、写、唱,想这几个服务的名称就花了好久 ~
我默认大家都会写 grpc 服务,如果不会写的,可以查看下我原来的文章《go grpc hello world》。
应用示例
实例化 tracer
func newjaegertracer(servicename string, jaegerhostport string) (opentracing.tracer, io.closer, error) { cfg := &jaegerconfig.configuration { sampler: &jaegerconfig.samplerconfig{ type : "const", //固定采样 param : 1, //1=全采样、0=不采样 }, reporter: &jaegerconfig.reporterconfig{ logspans : true, localagenthostport : jaegerhostport, }, servicename: servicename, } tracer, closer, err := cfg.newtracer(jaegerconfig.logger(jaeger.stdlogger)) if err != nil { panic(fmt.sprintf("error: cannot init jaeger: %v\n", err)) } opentracing.setglobaltracer(tracer) return tracer, closer, err }
http 注入
injecterr := jaeger.tracer.inject(span.context(), opentracing.httpheaders, opentracing.httpheaderscarrier(req.header)) if injecterr != nil { log.fatalf("%s: couldn't inject headers", err) }
http 拦截
spctx, err := opentracing.globaltracer().extract(opentracing.httpheaders, opentracing.httpheaderscarrier(c.request.header)) if err != nil { parentspan = tracer.startspan(c.request.url.path) defer parentspan.finish() } else { parentspan = opentracing.startspan( c.request.url.path, opentracing.childof(spctx), opentracing.tag{key: string(ext.component), value: "http"}, ext.spankindrpcserver, ) defer parentspan.finish() }
grpc 注入
func clientinterceptor(tracer opentracing.tracer, spancontext opentracing.spancontext) grpc.unaryclientinterceptor { return func(ctx context.context, method string, req, reply interface{}, cc *grpc.clientconn, invoker grpc.unaryinvoker, opts ...grpc.calloption) error { span := opentracing.startspan( "call grpc", opentracing.childof(spancontext), opentracing.tag{key: string(ext.component), value: "grpc"}, ext.spankindrpcclient, ) defer span.finish() md, ok := metadata.fromoutgoingcontext(ctx) if !ok { md = metadata.new(nil) } else { md = md.copy() } err := tracer.inject(span.context(), opentracing.textmap, mdreaderwriter{md}) if err != nil { span.logfields(log.string("inject-error", err.error())) } newctx := metadata.newoutgoingcontext(ctx, md) err = invoker(newctx, method, req, reply, cc, opts...) if err != nil { span.logfields(log.string("call-error", err.error())) } return err } }
grpc 拦截
func serverinterceptor(tracer opentracing.tracer) grpc.unaryserverinterceptor { return func(ctx context.context, req interface{}, info *grpc.unaryserverinfo, handler grpc.unaryhandler) (resp interface{}, err error) { md, ok := metadata.fromincomingcontext(ctx) if !ok { md = metadata.new(nil) } spancontext, err := tracer.extract(opentracing.textmap, mdreaderwriter{md}) if err != nil && err != opentracing.errspancontextnotfound { grpclog.errorf("extract from metadata err: %v", err) } else { span := tracer.startspan( info.fullmethod, ext.rpcserveroption(spancontext), opentracing.tag{key: string(ext.component), value: "grpc"}, ext.spankindrpcserver, ) defer span.finish() parentcontext = opentracing.contextwithspan(ctx, span) } return handler(parentcontext, req) } }
上面是一些核心的代码,涉及到的全部代码我都会上传到 github,供下载。
运行
启动服务
// 启动 listen 服务 cd listen && go run main.go // 启动 speak 服务 cd speak && go run main.go // 启动 read 服务 cd read && go run main.go // 启动 write 服务 cd write && go run main.go // 启动 sing 服务 cd sing && go run main.go // 启动 go-gin-api 服务 cd go-gin-api && go run main.go
访问路由
效果
就到这吧。
api 源码地址
service 源码地址
go-gin-api 系列文章
下一篇: html/form表单常用属性认识