Go语言微服务框架 - 9.分布式链路追踪-OpenTracing的初步引入

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
应用实时监控服务-可观测链路OpenTelemetry版,每月50GB免费额度
简介: 我们从API层到数据库层的链路已经打通,简单的CRUD功能已经可以快速实现。随着模块的增加,我们会越发感受到系统的复杂性,开始关注系统的可维护性。这时,有个名词会进入我们的视野:**分布式链路追踪**

我们从API层到数据库层的链路已经打通,简单的CRUD功能已经可以快速实现。

随着模块的增加,我们会越发感受到系统的复杂性,开始关注系统的可维护性。这时,有个名词会进入我们的视野:分布式链路追踪。相关的内容可以参考这我的两篇文章:

我们接下来直接进入实战。

v0.6.0:分布式链路追踪-OpenTracing的初步引入

项目链接 https://github.com/Junedayday/micro_web_service/tree/v0.6.0

目标

在项目中引入Jaeger为代表的OpenTracing,用一个traceid串联整个请求的链路。

关键技术点

  1. trace的初始化
  2. 将opentracing的设置到grpc和grpc-gateway中
  3. 将traceid引入到log组件中
  4. HTTP请求返回traceid

前两点我将一笔带过,在 https://junedayday.github.io/2021/10/20/readings/go-digest-3/ 这篇中已有详细的讲解

目录构造

--- micro_web_service            项目目录
    |-- gen                            从idl文件夹中生成的文件,不可手动修改
       |-- idl                             对应idl文件夹
          |-- demo                             对应idl/demo服务,包括基础结构、HTTP接口、gRPC接口
            |-- order                            对应idl/order服务,同上
    |-- idl                            原始的idl定义
       |-- demo                            业务package定义,protobuffer的原始定义
       |-- order                           业务order定义,同时干
    |-- internal                       项目的内部代码,不对外暴露
       |-- config                          配置相关的文件夹,viper的相关加载逻辑
       |-- dao                             Data Access Object层,是model层的实现
       |-- gormer                          从pkg/gormer中生成的相关代码,不允许更改
       |-- model                           model层,定义对象的接口方法,具体实现在dao层
       |-- mysql                           MySQL连接
       |-- server                          服务器的实现,对idl中定义服务的具体实现
       |-- service                         service层,作为领域实现的核心部分
     |-- zlog                            封装zap日志的代码实现
  |-- pkg                            开放给第三方的工具库
     |-- gormer                          gormer二进制工具,用于生成Gorm相关Dao层代码
    |-- buf.gen.yaml                   buf生成代码的定义,从v1beta升到v1
    |-- buf.yaml                       buf工具安装所需的工具,从v1beta升到v1
    |-- gen.sh                         生成代码的脚本:buf+gormer
    |-- go.mod                         Go Module文件
    |-- gormer.yaml                    将gormer中的参数移动到这里
    |-- main.go                        项目启动的main函数

1.trace的初始化

创建了一个jaeger的trace并设置到opentracing包里的全局变量中。

traceCfg := &jaegerconfig.Configuration{
   
  ServiceName: "MyService",
  Sampler: &jaegerconfig.SamplerConfig{
   
    Type:  jaeger.SamplerTypeConst,
    Param: 1,
  },
  Reporter: &jaegerconfig.ReporterConfig{
   
    LocalAgentHostPort: "127.0.0.1:6831",
    LogSpans:           true,
  },
}
tracer, closer, err := traceCfg.NewTracer(jaegerconfig.Logger(jaeger.StdLogger))
if err != nil {
   
  panic(err)
}
defer closer.Close()
opentracing.SetGlobalTracer(tracer)

2.将opentracing的设置到grpc和grpc-gateway中

利用了拦截器的特性,类似于middleware。

// grpc-gateway
opts := []grpc.DialOption{
   
  grpc.WithInsecure(),
  grpc.WithUnaryInterceptor(
    grpc_opentracing.UnaryClientInterceptor(
      grpc_opentracing.WithTracer(opentracing.GlobalTracer()),
    ),
  ),
}

if err := demo.RegisterDemoServiceHandlerFromEndpoint(ctx, mux, fmt.Sprintf(":%d", config.Viper.GetInt("server.grpc.port")), opts); err != nil {
   
  return errors.Wrap(err, "RegisterDemoServiceHandlerFromEndpoint error")
}

// grpc
s := grpc.NewServer(grpc.UnaryInterceptor(grpc_opentracing.UnaryServerInterceptor(grpc_opentracing.WithTracer(opentracing.GlobalTracer()))))

3.将traceid引入到log组件中

从Opentracing对Go语言的相关介绍可以得知,trace信息被放在go语言的context里。于是,就有了下面这一段提取traceid的代码。

// 为了使用方便,不修改zap源码,这里利用With函数返回一个SugaredLogger
func WithTrace(ctx context.Context) *zap.SugaredLogger {
   
    var jTraceId jaeger.TraceID
    if parent := opentracing.SpanFromContext(ctx); parent != nil {
   
        parentCtx := parent.Context()
        if tracer := opentracing.GlobalTracer(); tracer != nil {
   
            mySpan := tracer.StartSpan("my info", opentracing.ChildOf(parentCtx))
      // 提取出一个jaeger的traceid
            if sc, ok := mySpan.Context().(jaeger.SpanContext); ok {
   
                jTraceId = sc.TraceID()
            }
            defer mySpan.Finish()
        }
    }

    return Sugar.With(zap.String(jaeger.TraceContextHeaderName, fmt.Sprint(jTraceId)))
}

4.HTTP请求返回traceid

在拦截器里,解析出trace信息,设置到http的头里。

trace, ok := serverSpan.Context().(jaeger.SpanContext)
if ok {
   
  w.Header().Set(jaeger.TraceContextHeaderName, fmt.Sprint(trace.TraceID()))
}

示例

我们模拟一个简单的请求

curl --request GET 'http://127.0.0.1:8081/v1/orders'

从返回的结果来看,可以看到Uber-Trace-Id头里有个具体的trace-id,例如5fd1fc3ba1715909。

而在应用代码中,我们添加了一行日志:

func (orderSvc *OrderService) List(ctx context.Context, pageNumber, pageSize int, condition *gormer.OrderOptions) ([]gormer.Order, int64, error) {
   
    zlog.WithTrace(ctx).Infof("page number is %d", pageNumber)
    // zlog信息
    return orders, count, nil
}

具体的打印如下:

2021-10-22T17:25:05.591+0800    info    service/order.go:26    page number is 0    {"uber-trace-id": "5fd1fc3ba1715909"}

虽然格式还不是那么优美,但traceid信息已经填入到了日志中。

至此,调用方只要提供返回的trace-id,我们就可以在程序日志中查找到相应的日志信息,方便针对性地排查问题。

总结

OpenTracing是服务治理非常关键的一环。利用traceid串联一个请求的整个生命周期,能帮助我们快速地排查问题,在实际生产环境上能更快地定位问题。

相关实践学习
基于OpenTelemetry构建全链路追踪与监控
本实验将带领您快速上手可观测链路OpenTelemetry版,包括部署并接入多语言应用、体验TraceId自动注入至日志以实现调用链与日志的关联查询、以及切换调用链透传协议以满足全链路打通的需求。
分布式链路追踪Skywalking
Skywalking是一个基于分布式跟踪的应用程序性能监控系统,用于从服务和云原生等基础设施中收集、分析、聚合以及可视化数据,提供了一种简便的方式来清晰地观测分布式系统,具有分布式追踪、性能指标分析、应用和服务依赖分析等功能。 分布式追踪系统发展很快,种类繁多,给我们带来很大的方便。但在数据采集过程中,有时需要侵入用户代码,并且不同系统的 API 并不兼容,这就导致了如果希望切换追踪系统,往往会带来较大改动。OpenTracing为了解决不同的分布式追踪系统 API 不兼容的问题,诞生了 OpenTracing 规范。OpenTracing 是一个轻量级的标准化层,它位于应用程序/类库和追踪或日志分析程序之间。Skywalking基于OpenTracing规范开发,具有性能好,支持多语言探针,无侵入性等优势,可以帮助我们准确快速的定位到线上故障和性能瓶颈。 在本套课程中,我们将全面的讲解Skywalking相关的知识。从APM系统、分布式调用链等基础概念的学习加深对Skywalking的理解,从0开始搭建一套完整的Skywalking环境,学会对各类应用进行监控,学习Skywalking常用插件。Skywalking原理章节中,将会对Skywalking使用的agent探针技术进行深度剖析,除此之外还会对OpenTracing规范作整体上的介绍。通过对本套课程的学习,不止能学会如何使用Skywalking,还将对其底层原理和分布式架构有更深的理解。本课程由黑马程序员提供。
目录
相关文章
|
11天前
|
JavaScript Java Go
探索Go语言在微服务架构中的优势
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出。本文将深入探讨Go语言在构建微服务时的性能优势,包括其在内存管理、网络编程、并发模型以及工具链支持方面的特点。通过对比其他流行语言,我们将揭示Go语言如何成为微服务架构中的一股清流。
102 53
|
1月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
101 3
|
2月前
|
Go API Docker
热门go与微服务15
热门go与微服务15
32 2
|
2月前
|
安全 应用服务中间件 API
微服务分布式系统架构之zookeeper与dubbo-2
微服务分布式系统架构之zookeeper与dubbo-2
|
2月前
|
负载均衡 Java 应用服务中间件
微服务分布式系统架构之zookeeper与dubbor-1
微服务分布式系统架构之zookeeper与dubbor-1
|
5天前
|
存储 JSON 监控
微服务链路追踪原理,一文搞懂!
本文重点讲解微服务链路追踪(Microservices Distributed Tracing),介绍其原理、架构及工作流程。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
微服务链路追踪原理,一文搞懂!
|
8天前
|
监控 Go API
Go语言在微服务架构中的应用实践
在微服务架构的浪潮中,Go语言以其简洁、高效和并发处理能力脱颖而出,成为构建微服务的理想选择。本文将探讨Go语言在微服务架构中的应用实践,包括Go语言的特性如何适应微服务架构的需求,以及在实际开发中如何利用Go语言的特性来提高服务的性能和可维护性。我们将通过一个具体的案例分析,展示Go语言在微服务开发中的优势,并讨论在实际应用中可能遇到的挑战和解决方案。
|
9天前
|
Go 数据处理 API
Go语言在微服务架构中的应用与优势
本文摘要采用问答形式,以期提供更直接的信息获取方式。 Q1: 为什么选择Go语言进行微服务开发? A1: Go语言的并发模型、简洁的语法和高效的编译速度使其成为微服务架构的理想选择。 Q2: Go语言在微服务架构中有哪些优势? A2: 主要优势包括高性能、高并发处理能力、简洁的代码和强大的标准库。 Q3: 文章将如何展示Go语言在微服务中的应用? A3: 通过对比其他语言和展示Go语言在实际项目中的应用案例,来说明其在微服务架构中的优势。
|
18天前
|
存储 NoSQL 关系型数据库
微服务Zipkin链路追踪原理,图解版,一文吃透!
本文重点讲解Zipkin链路追踪的原理与使用,帮助解决微服务架构下的服务响应延迟等问题,提升系统性能与稳定性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
微服务Zipkin链路追踪原理,图解版,一文吃透!
|
28天前
|
Cloud Native Go API
Go语言在微服务架构中的创新应用与实践
本文深入探讨了Go语言在构建高效、可扩展的微服务架构中的应用。Go语言以其轻量级协程(goroutine)和强大的并发处理能力,成为微服务开发的首选语言之一。通过实际案例分析,本文展示了如何利用Go语言的特性优化微服务的设计与实现,提高系统的响应速度和稳定性。文章还讨论了Go语言在微服务生态中的角色,以及面临的挑战和未来发展趋势。