WEBKT

微服务gRPC可观测性改造:链路追踪与业务数据关联实战

94 0 0 0

背景

最近团队在搞微服务,大量使用了gRPC。爽是真爽,但问题也来了:服务拆得细,调用链那个复杂啊!出问题排查半天,心态都崩了。痛定思痛,决定搞一波gRPC的可观测性改造。

痛点分析

  • 跨服务调用链追踪困难:服务A调服务B,服务B再调服务C... 出了问题,你得一个个服务查日志,效率低下。
  • 业务数据与追踪信息脱节:光知道调用链不行,还得知道具体请求参数、返回值、错误码等信息,才能快速定位问题。
  • 侵入性改造风险:不想改太多代码,改多了容易出bug,影响现有业务。

解决方案

核心思路:利用gRPC的拦截器(Interceptor)机制,实现链路追踪信息的自动传递和业务数据的关联。

1. 链路追踪框架选型

市面上链路追踪框架不少,比如Jaeger、Zipkin、SkyWalking等。我们最终选择了SkyWalking,原因如下:

  • 社区活跃:文档完善,遇到问题容易找到答案。
  • 支持多种协议:除了gRPC,还支持HTTP、Kafka等。
  • 性能损耗低:对现有服务影响较小。

2. gRPC拦截器实现

gRPC拦截器分为客户端拦截器和服务端拦截器。

  • 客户端拦截器:负责在发起gRPC请求时,将链路追踪的上下文信息(Trace ID、Span ID等)添加到请求的Metadata中。

    type ClientInterceptor struct{}
    
    func (c *ClientInterceptor) UnaryClientInterceptor(
        ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption,
    ) error {
        // 从context中获取trace信息 (这里假设你已经用中间件把trace信息放到了context中)
        traceID := ctx.Value("trace_id")
        spanID := ctx.Value("span_id")
    
        // 将trace信息添加到metadata
        md := metadata.Pairs(
            "trace_id", traceID.(string),
            "span_id", spanID.(string),
        )
        ctx = metadata.NewOutgoingContext(ctx, md)
    
        // 调用gRPC方法
        err := invoker(ctx, method, req, reply, cc, opts...)
        return err
    }
    
  • 服务端拦截器:负责从请求的Metadata中提取链路追踪的上下文信息,并创建新的Span。

    type ServerInterceptor struct{}
    
    func (s *ServerInterceptor) UnaryServerInterceptor(
        ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler,
    ) (interface{}, error) {
        // 从metadata中获取trace信息
        md, ok := metadata.FromIncomingContext(ctx)
        if !ok {
            // 没有trace信息,创建一个新的
            // ...
        }
    
        traceID := md.Get("trace_id")[0]
        spanID := md.Get("span_id")[0]
    
        // 创建新的span
        // ...
    
        // 调用gRPC方法
        resp, err := handler(ctx, req)
    
        // 记录span结束时间
        // ...
    
        return resp, err
    }
    

3. 业务数据关联

除了链路追踪信息,我们还需要将一些关键的业务数据关联到追踪链路上,方便问题定位。例如:

  • 请求参数:记录gRPC请求的参数,方便了解请求的上下文。
  • 返回值:记录gRPC方法的返回值,方便判断请求是否成功。
  • 错误码:记录gRPC方法的错误码,方便定位错误原因。

实现方式:在拦截器中,将这些业务数据添加到SkyWalking的Tag中。

// 示例:在服务端拦截器中添加业务数据
span.SetTag("request.param", fmt.Sprintf("%v", req))
span.SetTag("response.code", "200") // 假设成功

4. 改造步骤

  1. 引入SkyWalking的gRPC插件:在每个gRPC服务中引入SkyWalking的gRPC插件。
  2. 注册拦截器:在gRPC服务器启动时,注册客户端和服务端拦截器。
  3. 添加业务数据关联代码:在拦截器中,添加业务数据关联代码。
  4. 部署上线:将改造后的服务部署上线。

5. 注意事项

  • 性能测试:改造完成后,进行性能测试,确保对现有服务的影响在可接受范围内。
  • 数据安全:敏感数据不要直接记录到追踪链路上,可以考虑加密或脱敏处理。
  • 日志规范:统一日志格式,方便后续的日志分析。

总结

通过gRPC拦截器机制,我们可以高效地实现gRPC服务的可观测性改造,将跨服务调用链追踪和业务数据关联起来,大大提高问题排查效率。虽然改造过程有些繁琐,但收益是巨大的。

希望这篇文章能帮助你更好地理解gRPC可观测性改造,少踩坑,早日实现服务监控自动化!

码农张三 gRPC可观测性链路追踪

评论点评