【GO】LGTM_Grafana_gozero_配置trace(4)_代码实操及追踪

2023-10-29 07:50

本文主要是介绍【GO】LGTM_Grafana_gozero_配置trace(4)_代码实操及追踪,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最近在尝试用 LGTM 来实现 Go 微服务的可观测性,就顺便整理一下文档。

Tempo 会分为 4 篇文章:

  1. Tempo 的架构
  2. 官网测试实操跑通
  3. gin 框架发送 trace 数据到 tempo
  4. go-zero 微服务框架发送数据到 tempo

本文就是写一下如何在 go-zero 微服务框架里面配置 trace 及 trace 的 handler 在哪里

代码地址:lgtm/example/go-zero-lgtm at main · zxmfke/lgtm (github.com)

这个代码里面包含一个 order 的 api 服务和一个 user 的 rpc 服务


go-zero: 1.5.5
go: 1.20.3


go-zero 的服务接入 trace,是比较简单的,只需要在配置项中,配置 Telemetry 里面的参数就可以了。

Telemetry 配置

在 /etc 下有配置文件,其中配置:

...Telemetry:Name: "order"Endpoint: "127.0.0.1:4318"Batcher: "otlphttp"...

源码(core/trace/config.go)中可以配置的参数:

type Config struct {Name     string  `json:",optional"`Endpoint string  `json:",optional"`Sampler  float64 `json:",default=1.0"`Batcher  string  `json:",default=jaeger,options=jaeger|zipkin|otlpgrpc|otlphttp|file"`// OtlpHeaders represents the headers for OTLP gRPC or HTTP transport.// For example://  uptrace-dsn: 'http://project2_secret_token@localhost:14317/2'OtlpHeaders map[string]string `json:",optional"`// OtlpHttpPath represents the path for OTLP HTTP transport.// For example// /v1/tracesOtlpHttpPath string `json:",optional"`// Disabled indicates whether StartAgent starts the agent.Disabled bool `json:",optional"`
}

主要就是配置:

  • Name:服务名称
  • Endpoint:Trace provider 的 IP 地址
  • Batcher:用哪一种 provider 类型

注意,Disabled 如果没有设置为 true,就默认表示发送 trace 数据

Grafana Tempo Trace Data

请求接口后,trace 数据会发送到 Grafana 的 Tempo 里面,如下图:

在这里插入图片描述

可以看到分为三段:

  • order-api 收到请求的时候
  • order-api 请求 user-rpc 的时候
  • user-rpc 收到请求的时候

接下来我们可以追踪一下源码看看是在哪里发送的 trace 数据

追踪代码
初始化

order

order 是 api 服务,Telemetry 初始化的逻辑:

  • order.go

    // L24
    server := rest.MustNewServer(c.RestConf)
    
  • go-zero/rest/server.go

    // L35
    func MustNewServer(c RestConf, opts ...RunOption) *Server {server, err := NewServer(c, opts...)...
    }// L46
    func NewServer(c RestConf, opts ...RunOption) (*Server, error) {if err := c.SetUp(); err != nil {return nil, err}...return server, nil
    }
    
  • go-zero/core/sevice/serviceconf.go

    // L44
    func (sc ServiceConf) SetUp() error {...trace.StartAgent(sc.Telemetry)...return nil
    }
    
  • go-zero/core/trace/agent.go

    // L39
    func StartAgent(c Config) {...// if error happens, let later calls run.if err := startAgent(c); err != nil {return}...
    }
    

user

  • user.go

    // L29
    s := zrpc.MustNewServer(c.RpcServerConf, func(grpcServer *grpc.Server) {userclient.RegisterUserServer(grpcServer, srv)if c.Mode == service.DevMode || c.Mode == service.TestMode {reflection.Register(grpcServer)}
    })
    defer s.Stop()
    
  • go-zero/zrpc/server.go

    // L23 
    func MustNewServer(c RpcServerConf, register internal.RegisterFn) *RpcServer {server, err := NewServer(c, register)logx.Must(err)return server
    }// L30
    func NewServer(c RpcServerConf, register internal.RegisterFn) (*RpcServer, error) {...if err = c.SetUp(); err != nil {return nil, err}return rpcServer, nil
    }
    
  • Setup() 跟 order 的一样了

Order api 的 trace handler

之前看过源码,go-zero 的 api 和 rpc 是分开两个目录,所以 api 的代码一定在 go-zero/rest 下面。 要在请求的收到的时候发送一个 trace 数据,只有可能以中间件的形式加入,因此可以找到 rest 下的 handler 文件夹,里面就有一个 tracinghandler.go。

源码位置:go-zero/rest/handler/tracinghandler.go

这个就类似上一篇的 gin server 加 trace middleware 一样。

func TracingHandler(serviceName, path string) func(http.Handler) http.Handler {return func(next http.Handler) http.Handler {propagator := otel.GetTextMapPropagator()tracer := otel.GetTracerProvider().Tracer(trace.TraceName)return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {ctx := propagator.Extract(r.Context(), propagation.HeaderCarrier(r.Header))spanName := pathif len(spanName) == 0 {spanName = r.URL.Path}spanCtx, span := tracer.Start(ctx,spanName,oteltrace.WithSpanKind(oteltrace.SpanKindServer),oteltrace.WithAttributes(semconv.HTTPServerAttributesFromHTTPRequest(serviceName, spanName, r)...),)defer span.End()// convenient for tracking error messagespropagator.Inject(spanCtx, propagation.HeaderCarrier(w.Header()))next.ServeHTTP(w, r.WithContext(spanCtx))})}
}
User rpc 的 trace handler

同理,user 是 rpc 服务,所以就到 zrpc 下面找,按照惯性,找到 go-zero/zrpc/internal/serverinterceptors ,这个时候就可以看到 tracinginterceptor.go

源码位置:go-zero/zrpc/internal/serverinterceptors/tracinginterceptor.go

func UnaryTracingInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,handler grpc.UnaryHandler) (interface{}, error) {ctx, span := startSpan(ctx, info.FullMethod)defer span.End()ztrace.MessageReceived.Event(ctx, 1, req)resp, err := handler(ctx, req)if err != nil {s, ok := status.FromError(err)if ok {span.SetStatus(codes.Error, s.Message())span.SetAttributes(ztrace.StatusCodeAttr(s.Code()))ztrace.MessageSent.Event(ctx, 1, s.Proto())} else {span.SetStatus(codes.Error, err.Error())}return nil, err}span.SetAttributes(ztrace.StatusCodeAttr(gcodes.OK))ztrace.MessageSent.Event(ctx, 1, resp)return resp, nil
}

找到这个,但是没有发现 order-api 调用 user 的 handler。就很奇怪,又返回 rest 里面找,不过还是没找到,后面才发现 zrpc/internal 下面有一个 clientinterceptors ,原来在这边。

func UnaryTracingInterceptor(ctx context.Context, method string, req, reply interface{},cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {ctx, span := startSpan(ctx, method, cc.Target())defer span.End()ztrace.MessageSent.Event(ctx, 1, req)ztrace.MessageReceived.Event(ctx, 1, reply)if err := invoker(ctx, method, req, reply, cc, opts...); err != nil {s, ok := status.FromError(err)if ok {span.SetStatus(codes.Error, s.Message())span.SetAttributes(ztrace.StatusCodeAttr(s.Code()))} else {span.SetStatus(codes.Error, err.Error())}return err}span.SetAttributes(ztrace.StatusCodeAttr(gcodes.OK))return nil
}

ror(err)
if ok {
span.SetStatus(codes.Error, s.Message())
span.SetAttributes(ztrace.StatusCodeAttr(s.Code()))
} else {
span.SetStatus(codes.Error, err.Error())
}
return err
}

span.SetAttributes(ztrace.StatusCodeAttr(gcodes.OK))
return nil

}


这篇关于【GO】LGTM_Grafana_gozero_配置trace(4)_代码实操及追踪的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/299263

相关文章

如何使用Nginx配置将80端口重定向到443端口

《如何使用Nginx配置将80端口重定向到443端口》这篇文章主要为大家详细介绍了如何将Nginx配置为将HTTP(80端口)请求重定向到HTTPS(443端口),文中的示例代码讲解详细,有需要的小伙... 目录1. 创建或编辑Nginx配置文件2. 配置HTTP重定向到HTTPS3. 配置HTTPS服务器

SpringBoot中配置Redis连接池的完整指南

《SpringBoot中配置Redis连接池的完整指南》这篇文章主要为大家详细介绍了SpringBoot中配置Redis连接池的完整指南,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以... 目录一、添加依赖二、配置 Redis 连接池三、测试 Redis 操作四、完整示例代码(一)pom.

Linux内核参数配置与验证详细指南

《Linux内核参数配置与验证详细指南》在Linux系统运维和性能优化中,内核参数(sysctl)的配置至关重要,本文主要来聊聊如何配置与验证这些Linux内核参数,希望对大家有一定的帮助... 目录1. 引言2. 内核参数的作用3. 如何设置内核参数3.1 临时设置(重启失效)3.2 永久设置(重启仍生效

IDEA自动生成注释模板的配置教程

《IDEA自动生成注释模板的配置教程》本文介绍了如何在IntelliJIDEA中配置类和方法的注释模板,包括自动生成项目名称、包名、日期和时间等内容,以及如何定制参数和返回值的注释格式,需要的朋友可以... 目录项目场景配置方法类注释模板定义类开头的注释步骤类注释效果方法注释模板定义方法开头的注释步骤方法注

使用Java将DOCX文档解析为Markdown文档的代码实现

《使用Java将DOCX文档解析为Markdown文档的代码实现》在现代文档处理中,Markdown(MD)因其简洁的语法和良好的可读性,逐渐成为开发者、技术写作者和内容创作者的首选格式,然而,许多文... 目录引言1. 工具和库介绍2. 安装依赖库3. 使用Apache POI解析DOCX文档4. 将解析

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

如何在Mac上安装并配置JDK环境变量详细步骤

《如何在Mac上安装并配置JDK环境变量详细步骤》:本文主要介绍如何在Mac上安装并配置JDK环境变量详细步骤,包括下载JDK、安装JDK、配置环境变量、验证JDK配置以及可选地设置PowerSh... 目录步骤 1:下载JDK步骤 2:安装JDK步骤 3:配置环境变量1. 编辑~/.zshrc(对于zsh

go 指针接收者和值接收者的区别小结

《go指针接收者和值接收者的区别小结》在Go语言中,值接收者和指针接收者是方法定义中的两种接收者类型,本文主要介绍了go指针接收者和值接收者的区别小结,文中通过示例代码介绍的非常详细,需要的朋友们下... 目录go 指针接收者和值接收者的区别易错点辨析go 指针接收者和值接收者的区别指针接收者和值接收者的

售价599元起! 华为路由器X1/Pro发布 配置与区别一览

《售价599元起!华为路由器X1/Pro发布配置与区别一览》华为路由器X1/Pro发布,有朋友留言问华为路由X1和X1Pro怎么选择,关于这个问题,本期图文将对这二款路由器做了期参数对比,大家看... 华为路由 X1 系列已经正式发布并开启预售,将在 4 月 25 日 10:08 正式开售,两款产品分别为华

SQL server配置管理器找不到如何打开它

《SQLserver配置管理器找不到如何打开它》最近遇到了SQLserver配置管理器打不开的问题,尝试在开始菜单栏搜SQLServerManager无果,于是将自己找到的方法总结分享给大家,对SQ... 目录方法一:桌面图标进入方法二:运行窗口进入方法三:查找文件路径方法四:检查 SQL Server 安