go-kit 微服务 服务链路追踪(jaeger 实现)(1)
- 对grpc调用添加链路追踪
部署 jaeger
-
生产环境部署
- Docker Hub 中有官方打好的 Image https://hub.docker.com/u/jaegertracing/
-
本地测试
-
可以直接用 Jaeger 的 all-in-one
```base
sudo docker pull jaegertracing/all-in-one sudo 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 ```
-
能正常访问 http://127.0.0.1:16686/ 则安装成功
-
编写jaeger 关于grpc代码
初始化客户端
func NewJaegerTracer(serviceName string) (tracer opentracing.Tracer, closer io.Closer, err error) {
cfg := &jaegerConfig.Configuration{
Sampler: &jaegerConfig.SamplerConfig{
Type: "const", //固定采样
Param: 1, //1=全采样、0=不采样
},
Reporter: &jaegerConfig.ReporterConfig{
LogSpans: true,
LocalAgentHostPort: "127.0.0.1:6831",
},
ServiceName: serviceName,
}
tracer, closer, err = cfg.NewTracer(jaegerConfig.Logger(jaeger.StdLogger))
if err != nil {
return
}
opentracing.SetGlobalTracer(tracer)
return
}
grpc 客户端中间件
func JaegerClientMiddleware(tracer opentracing.Tracer) grpc.UnaryClientInterceptor {
return func(
ctx context.Context,
method string,
req, resp interface{},
cc *grpc.ClientConn,
invoker grpc.UnaryInvoker,
opts ...grpc.CallOption,
) error {
var parentCtx opentracing.SpanContext
//先判断ctx里面有没有 span 信息
//没有就生成一个
if parent := opentracing.SpanFromContext(ctx); parent != nil {
parentCtx = parent.Context()
}
cliSpan := tracer.StartSpan(
method,
opentracing.ChildOf(parentCtx),//父子关系的span关系
TracingComponentTag,//grcp tag
ext.SpanKindRPCClient,//客户端 tag
)
defer cliSpan.Finish()
//从context中获取metadata。md.(type) == map[string][]string
md, ok := metadata.FromOutgoingContext(ctx)
if !ok {
md = metadata.New(nil)
} else {
////如果对metadata进行修改,那么需要用拷贝的副本进行修改。
md = md.Copy()
}
//定义一个carrier,下面的Inject注入数据需要用到。carrier.(type) == map[string]string
//carrier := opentracing.TextMapCarrier{}
mdWriter := MDReaderWriter{md}
////将span的context信息注入到carrier中
err := tracer.Inject(cliSpan.Context(), opentracing.TextMap, mdWriter)
if err != nil {
grpclog.Errorf("inject to metadata err %v", err)
}
////创建一个新的context,把metadata附带上
ctx = metadata.NewOutgoingContext(ctx, md)
err = invoker(ctx, method, req, resp, cc, opts...)
if err != nil {
cliSpan.LogFields(log.String("err", err.Error()))
}
return err
}
}
grpc 服务端中间件
func JaegerServerMiddleware(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)
}
serverSpan := tracer.StartSpan(
info.FullMethod,
ext.RPCServerOption(spanContext),
TracingComponentTag,
ext.SpanKindRPCServer,
)
defer serverSpan.Finish()
ctx = opentracing.ContextWithSpan(ctx, serverSpan)
return handler(ctx, req)
}
}
修改服务端方法
......
tracer, _, err := utils.NewJaegerTracer("user_agent_server")
if err != nil {
utils.GetLogger().Warn("[user_agent] NewJaegerTracer", zap.Error(err))
quitChan <- err
}
Registar.Register()
utils.GetLogger().Info("[user_agent] grpc run " + *grpcAddr)
chainUnaryServer := grpcmiddleware.ChainUnaryServer(
grpctransport.Interceptor,
tils.JaegerServerMiddleware(tracer),
)
baseServer := grpc.NewServer(grpc.UnaryInterceptor(chainUnaryServer))
pb.RegisterUserServer(baseServer, grpcServer)
quitChan <- baseServer.Serve(grpcListener)
......
修改客户端代码
......
tracer, _, err := utils.NewJaegerTracer("user_agent_client")
if err != nil {
return nil, err
}
......
......
conn, err := grpc.Dial(instance, grpc.WithInsecure(),
grpc.WithUnaryInterceptor(utils.JaegerClientMiddleware(u.tracer)), )
if err != nil {
return nil, nil, err
}
srv := u.NewGRPCClient(conn)
......
运行
- 运行 TestNewUserAgentClient 方法
- 我们登录 http://127.0.0.1:16686/ jaeger后台查询信息
结语
- 通过后台界面我们可以看到请求信息,以便与我们对服务的了解
- jaeger的用法还有很多,这里只展示简单的使用,更加高级的功能欢迎大家一起讨论
- 欢迎添加QQ一起讨论