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

本文涉及的产品
可观测链路 OpenTelemetry 版,每月50GB免费额度
日志服务 SLS,月写入数据量 50GB 1个月
简介: 我们从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,还将对其底层原理和分布式架构有更深的理解。本课程由黑马程序员提供。
目录
相关文章
|
22天前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
82 3
|
2月前
|
安全 应用服务中间件 API
微服务分布式系统架构之zookeeper与dubbo-2
微服务分布式系统架构之zookeeper与dubbo-2
|
2月前
|
负载均衡 Java 应用服务中间件
微服务分布式系统架构之zookeeper与dubbor-1
微服务分布式系统架构之zookeeper与dubbor-1
|
19天前
|
Dubbo Java 应用服务中间件
Dubbo学习圣经:从入门到精通 Dubbo3.0 + SpringCloud Alibaba 微服务基础框架
尼恩团队的15大技术圣经,旨在帮助开发者系统化、体系化地掌握核心技术,提升技术实力,从而在面试和工作中脱颖而出。本文介绍了如何使用Dubbo3.0与Spring Cloud Gateway进行整合,解决传统Dubbo架构缺乏HTTP入口的问题,实现高性能的微服务网关。
|
18天前
|
消息中间件 存储 负载均衡
微服务与分布式系统设计看这篇就够了!
【10月更文挑战第12天】 在现代软件架构中,微服务和分布式系统设计已经成为构建可扩展、灵活和可靠应用程序的主流方法。本文将深入探讨微服务架构的核心概念、设计原则和挑战,并提供一些关于如何在分布式系统中实现微服务的实用指导。
38 2
|
19天前
|
人工智能 文字识别 Java
SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?
尼恩,一位拥有20年架构经验的老架构师,通过其深厚的架构功力,成功指导了一位9年经验的网易工程师转型为大模型架构师,薪资逆涨50%,年薪近80W。尼恩的指导不仅帮助这位工程师在一年内成为大模型架构师,还让他管理起了10人团队,产品成功应用于多家大中型企业。尼恩因此决定编写《LLM大模型学习圣经》系列,帮助更多人掌握大模型架构,实现职业跃迁。该系列包括《从0到1吃透Transformer技术底座》、《从0到1精通RAG架构》等,旨在系统化、体系化地讲解大模型技术,助力读者实现“offer直提”。此外,尼恩还分享了多个技术圣经,如《NIO圣经》、《Docker圣经》等,帮助读者深入理解核心技术。
SpringCloud+Python 混合微服务,如何打造AI分布式业务应用的技术底层?
|
2月前
|
Kubernetes Java Android开发
用 Quarkus 框架优化 Java 微服务架构的设计与实现
Quarkus 是专为 GraalVM 和 OpenJDK HotSpot 设计的 Kubernetes Native Java 框架,提供快速启动、低内存占用及高效开发体验,显著优化了 Java 在微服务架构中的表现。它采用提前编译和懒加载技术实现毫秒级启动,通过优化类加载机制降低内存消耗,并支持多种技术和框架集成,如 Kubernetes、Docker 及 Eclipse MicroProfile,助力开发者轻松构建强大微服务应用。例如,在电商场景中,可利用 Quarkus 快速搭建商品管理和订单管理等微服务,提升系统响应速度与稳定性。
55 5
|
2月前
|
缓存 Java 应用服务中间件
随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架
【9月更文挑战第6天】随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架。Nginx作为高性能的HTTP反向代理服务器,常用于前端负载均衡,提升应用的可用性和响应速度。本文详细介绍如何通过合理配置实现Spring Boot与Nginx的高效协同工作,包括负载均衡策略、静态资源缓存、数据压缩传输及Spring Boot内部优化(如线程池配置、缓存策略等)。通过这些方法,开发者可以显著提升系统的整体性能,打造高性能、高可用的Web应用。
70 2
|
2月前
|
Kubernetes Go Docker
掌握微服务架构:从Go到容器化的旅程
摘要,通常简短概述文章内容,要求精炼。在本文中,我们将打破常规,采用一种故事化叙述的摘要,旨在激发读者的好奇心和探究欲: “从宁静的海滨小城出发,我们踏上了一场技术探险之旅,探索微服务架构的奥秘。我们将学习如何用Go编写微服务,以及如何通过Docker和Kubernetes将它们打包进小巧的容器中。在这场旅程中,我们将遇到挑战、收获知识,最终实现应用的快速部署与可扩展性。”
|
3月前
|
Kubernetes Cloud Native Docker
云原生之旅:从容器到微服务的架构演变
【8月更文挑战第29天】在数字化时代的浪潮下,云原生技术以其灵活性、可扩展性和弹性管理成为企业数字化转型的关键。本文将通过浅显易懂的语言和生动的比喻,带领读者了解云原生的基本概念,探索容器化技术的奥秘,并深入微服务架构的世界。我们将一起见证代码如何转化为现实中的服务,实现快速迭代和高效部署。无论你是初学者还是有经验的开发者,这篇文章都会为你打开一扇通往云原生世界的大门。

热门文章

最新文章