暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

链路追踪建设分享

代码炼金工坊 2021-03-08
2524

链路追踪建设分享

本文为笔者在去年工作中实践后的总结分享

the jaeger is tracing the prey

背景

开发跨部门权限问题,无法在线上(测试、预发布、正式环境)查看容器内错误日志微服务化,即使有权限查看容器日志,业务出现bug时排查定位问题所在的微服务很慢很麻烦被动发现问题。比如说某些api的请求出现异常,被动等待下游反馈

链路追踪如何解决问题

上报每一个请求和响应的详细数据,包括header头,路由,请求参数,请求体,通信状态,以及支持自定义更多参数上报。阿里采集,提供查询接口,开发可以另外搭建查询服务独立查询,无需权限采用OpenTracing标准,分布式追踪,可以跨语言跨服务形成链路调用日志,一个请求经过了哪些服务,在哪里发生了故障被截断一目了然通过请求和响应状态可以为链路日志打上状态标签,然后上报到Prometheus统计指标。可以另外扩展出指标异常统计告警机制,及时主动获知错误发生,在下游反馈前解决问题

原理简述

jaeger_query

Jaeger参考文档[1]

header头注入标识,支持多种标识(b3
,uber-trace-id
等)。我们采用jaeger默认的uber-trace-id
标记链路id。
id值格式为:{trace-id}:{span-id}:{parent-span-id}:{flags}
trace-id:链路追踪id,一次请求从头到尾的唯一标识span-id:span的标识。span是链路追踪的基本单位,表示一个独立的工作单元,可以是一次函数调用,对一个服务的请求等。记录了服务名称,开始结束时间,以及服务打上的tag(上文说的状态异常标签,error=true
),logs(记录请求和响应的详细数据)
parent-span-id:父span标识flags:采样标志
作为服务端,接收请求的时候检测是否有uber-trace-id
标识,如果没有就创建一个,如果有则获取值并按照格式拆分,然后把请求中的span-id
改成自身的parent-span-id
,生成一个新的span-id
作为客户端,对其他服务发起调用,在请求头注入uber-trace-id
标识,传递给被调用服务
服务在一次请求和响应完成后独自上报span一个trace由多个span组成,表示了一次完整的链路追踪,按照span的父子关系向下排列,以时间轴的方式展示。

落地实践

原子服务集成上报中间件部署Jaeger服务端,包括Jaeger Agent和Jaegger CollectorAgent采用udp协议与客户端(原子服务)通信,无状态,消耗低,适用每个请求都上报的场景。客户端发送就完事,不关心Agent是否收到Agent收集一定量链路日志后批量上报给Collector,用的是http协议,然后Collector再写入到数据库里阿里提供了日志数据库存储服务。链路日志存储到第三方通过另外部署的Jaeger Query客户端读取阿里存储的日志,完全和生产服务解耦。可以用默认的客户端查询,或者将客户端暴露的api集成到Grafana查阅。go服务端采用的第三方上报工具内置了指标暴露接口,可以被promehteus采集统计请求状态

GIN中间件

gin的中间件需要自己实现:

代码库地址:yuchanns/bullets[2]

go get -u github.com/yuchanns/bullets

使用中间件

package main

import (
"context"
"github.com/gin-gonic/gin"
"github.com/yuchanns/bullets/common"
"github.com/yuchanns/bullets/common/middlewares"
"os"
)

func main() {
g := gin.Default()
// 服务名
serviceName := "openapi-service"
// 上报agent地址
agentAddr := os.Getenv("OPENTRACING_AGENT")
// 操作前缀
operationPrefix := []byte("api-request-")
opentracerCloseFunc, opentracerMiddleware, err := middlewares.BuildOpenTracerInterceptor(serviceName, agentAddr, operationPrefix)
if err != nil {
common.Logger.Error(context.Background(), err)
} else {
defer opentracerCloseFunc()
g.Use(opentracerMiddleware)
}
}

自定义打tag

import (
"github.com/gin-gonic/gin"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/log"
"github.com/pkg/errors"
)

func CustomTag(ctx *gin.Context) {
if cspan, ok := ctx.Get("tracing-context"); ok {
if span, ok := cspan.(opentracing.Span); ok {
span.SetTag("error", true)
span.LogFields(log.Error(errors.New("err")))
span.LogFields(log.String("exampleKey", "stringValue"))
}
}
}

GRPC中间件

代码库地址:yuchanns/gobyexample/grpc-app[3]

grpc生态系统[4]自带中间件,使用起来较为方便:

package main

import (
"io"
"log"
"os"

grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
grpc_recovery "github.com/grpc-ecosystem/go-grpc-middleware/recovery"
"github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc"
"github.com/opentracing/opentracing-go"
"github.com/uber/jaeger-client-go"
"google.golang.org/grpc"
)

func NewJaegerTracer(name, hostPort string) (opentracing.Tracer, io.Closer, error) {
tp, err := jaeger.NewUDPTransport(hostPort, 0)
if err != nil {
return nil, nil, err
}
reporter := jaeger.NewRemoteReporter(tp)

var sampler jaeger.Sampler
sampler = jaeger.NewConstSampler(true)

tracer, closer := jaeger.NewTracer(name,
sampler,
reporter,
)
opentracing.SetGlobalTracer(tracer)

return tracer, closer, nil
}

func main() {
tracer, closer, err := NewJaegerTracer("testApp", os.Getenv("AGENT_HOST_PORT"))
if err != nil {
log.Fatalf("failed to create Jaeger tracer")
}
defer closer.Close()

opts := []grpc.ServerOption{
grpc.StreamInterceptor(grpc_middleware.ChainStreamServer(
otgrpc.OpenTracingStreamServerInterceptor(tracer, otgrpc.LogPayloads()),
grpc_recovery.StreamServerInterceptor(),
)),
grpc.UnaryInterceptor(grpc_middleware.ChainUnaryServer(
otgrpc.OpenTracingServerInterceptor(tracer, otgrpc.LogPayloads()),
grpc_recovery.UnaryServerInterceptor(),
)),
}

srv := grpc.NewServer(opts...)

// ...register your grpc server

if err := srv.Serve(s.l); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}

引用链接

[1]
 Jaeger参考文档: https://www.jaegertracing.io/docs/1.11/client-libraries/
[2]
 yuchanns/bullets: https://github.com/yuchanns/bullets
[3]
 yuchanns/gobyexample/grpc-app: https://github.com/yuchanns/gobyexample/tree/monorepo/grpc-app
[4]
 grpc生态系统: https://github.com/grpc-ecosystem


文章转载自代码炼金工坊,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论