大项目中jaeger的使用 | 青训营

125 阅读6分钟

jaeger

官方示例项目

基本介绍

jeager 是一个分布式追踪工具,在微服务架构中运用尤为广泛,在 微服务架构中通常有几个上百个微服务,一个完整的业务流程通常由多个微服务来协同完成,即便是相同的业务流程,不同的请求所经过的微服务也不尽相同,这就给错误分析和性能瓶颈分析带来了大困难。分部署追踪是解决上述问题的常见做法,分布式追踪是一个比较大的话题,所涉及的规范、api、第三方工具比较多,目前比较主流的是 opentracing 规范。 jaeger(Go开发) 和 zipkin(Java开发) 是目前流行的开源实现。 jaeger 提供了 对 opentracing 的支持,确保了与其他工具的兼容性。

  • Jaeger客户端是OpenTracing API的特定于语言的实现。它们可用于手动或通过与OpenTracing集成的各种现有开源框架(例如Flask,Dropwizard,gRPC等)来检测应用程序以进行分布式跟踪。

    检测服务在接收新请求时创建跨度,并将上下文信息(跟踪ID,跨度ID和行李)附加到传出请求。

  • Jaeger Agent是一个网络守护程序,它侦听通过UDP发送的跨度,然后将其分批发送给收集器。它旨在作为基础结构组件部署到所有主机。该代理将收集器的路由和发现抽象到远离客户端的位置。

  • Jaeger Collector从Jaeger代理接收跟踪,并通过处理管道运行它们。

  • Jaeger Query 是一项从存储中检索跟踪并托管UI来显示跟踪的服务。

OpenTracing 基本概念
  • 一个 trace 可以被认为是由一个或多个 span 组成的有向无环图。
单个 trace(链路) 中 span 之间的关系
​
        [Span A]  ←←←(the root span)
            |
     +------+------+
     |             |
 [Span B]      [Span C] ←←←(Span C is a `ChildOf` Span A)
     |             |
 [Span D]      +---+-------+
               |           |
           [Span E]    [Span F] >>> [Span G] >>> [Span H]
                                       ↑
                                       ↑
                                       ↑
                         (Span G `FollowsFrom` Span F)
  • Span 是一次链路追踪里的基本组成元素
  • SpanContext 携带跨进程(跨服务)通信的数据
  • References : OpenTracing 目前定义了 2 种关系:ChildOfFollowsFrom
运行jaeger配置信息
#!/bin/sh
docker run -d --name jaeger \
  -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \
  -p 5775:5775/udp \
  -p 6831:6831/udp \
  -p 6832:6832/udp \
  -p 5778:5778 \
  -p 16686:16686 \
  -p 14268:14268 \
  -p 14250:14250 \
  -p 9411:9411 \
jaegertracing/all-in-one:1.25

这个 jaeger_run.sh 脚本用于在 Docker 中运行 Jaeger 全部组件。

这一行运行了一个 Docker 容器,基于 jaegertracing/all-in-one:1.25 镜像。具体的参数说明如下:

  • -d:以后台模式运行容器(detached mode)。

  • --name jaeger:将容器命名为 "jaeger"。

  • -e COLLECTOR_ZIPKIN_HOST_PORT=:9411:设置环境变量 COLLECTOR_ZIPKIN_HOST_PORT,指定 Zipkin 收集器的主机端口为 :9411

  • -p 参数是用于指定端口映射:

    • -p 5775:5775/udp:映射容器中的 UDP 端口 5775 到主机的 UDP 端口 5775。
    • -p 6831:6831/udp:映射容器中的 UDP 端口 6831 到主机的 UDP 端口 6831。
    • -p 6832:6832/udp:映射容器中的 UDP 端口 6832 到主机的 UDP 端口 6832。
    • -p 5778:5778:映射容器中的 TCP 端口 5778 到主机的 TCP 端口 5778。
    • -p 16686:16686:映射容器中的 TCP 端口 16686 到主机的 TCP 端口 16686。
    • -p 14268:14268:映射容器中的 TCP 端口 14268 到主机的 TCP 端口 14268。
    • -p 14250:14250:映射容器中的 TCP 端口 14250 到主机的 TCP 端口 14250。
    • -p 9411:9411:映射容器中的 TCP 端口 9411 到主机的 TCP 端口 9411。

这些端口的映射是为了让主机可以与 Jaeger 容器中的各个组件进行通信。

最终,通过 jaegertracing/all-in-one:1.25 镜像运行 Jaeger 的全部组件。这个镜像是一个包含了所有 Jaeger 组件的单容器部署。

脚本的作用是使用 Docker 运行 Jaeger 全部组件,以便可以在主机上通过映射的端口访问和使用 Jaeger UI,并与其他应用程序集成进行分布式追踪和性能监控。

Jaeger初始化
import (
    hertztracer "github.com/hertz-contrib/tracer/hertz"
    "github.com/uber/jaeger-client-go"
    "github.com/opentracing/opentracing-go"
    jaegercfg "github.com/uber/jaeger-client-go/config"
)
// InitJaeger ...
func InitJaeger(service string) io.Closer {
    cfg, _ := jaegercfg.FromEnv()/* 读取环境变量 */
    cfg.ServiceName = service
    tracer, closer, err := cfg.NewTracer(jaegercfg.Logger(jaeger.StdLogger))
    if err != nil {
        panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err))
    }
    opentracing.InitGlobalTracer(tracer)
    return closer
}
  • 通过调用 jaegercfg.FromEnv() 从环境变量中读取 Jaeger 的配置信息,创建一个配置对象 cfg。该函数会根据预定义的环境变量来解析和设置 Jaeger 的配置选项。
  • 如果读取配置时发生错误,函数将返回 nil 追踪器、nil 追踪器关闭器(io.Closer)和一个错误信息。
  • 使用 cfg.NewTracer() 方法创建一个 Jaeger 追踪器。该方法接受一个可选的 Logger 参数,用于设置日志记录器。在这里,我们使用 jaegercfg.Logger(jaeger.StdLogger) 设置日志记录器为标准输出。
  • 如果创建追踪器时发生错误,函数将返回 nil 追踪器、nil 追踪器关闭器和一个错误信息。
  • 在成功创建追踪器后,函数将返回创建的追踪器、一个可用于关闭追踪器的关闭器(io.Closer)和 nil 错误。

总结来说,InitJaeger() 函数用于初始化和创建 Jaeger 追踪器,并通过读取环境变量中的配置信息来设置追踪器的选项。它为应用程序提供了一种方便的方式来集成和使用 Jaeger 追踪器,用于记录和追踪分布式系统中的请求调用和性能信息。

kitex-client
    ht, hc := InitTracer("hertz-server")
    kt, kc := InitTracer("kitex-client")
    defer hc.Close()
    defer kc.Close()
​
    // kitex-client configure tracer
    client, err := echo.NewClient("echo",
        kclient.WithHostPorts("0.0.0.0:5555"),
        /* 这样,每个请求在发起时都会根据服务名和方法名创建一个唯一的操作名称(span name),以便在追踪系统中追踪和展示这些操作的调用信息。 */
        kclient.WithSuite(kopentracing.NewClientSuite(kt, func(c context.Context) string {
            endpoint := rpcinfo.GetRPCInfo(c).From()// 通过 From() 方法获取发起请求的远程终点(Endpoint)
            return endpoint.ServiceName() + "::" + endpoint.Method()
        })))
    if err != nil {
        panic(err)
    }
hertz server使用server.WithTracer() 配置添加 tracer
// hertz-server configure tracer
    h := server.Default(server.WithTracer(hertztracer.NewTracer(ht, func(c *app.RequestContext) string {
        return "test.hertz.server" + "::" + c.FullPath()
    })))
​
    // Register and use tracer middleware.
    // This middleware is simple demo.
    // You can refer to the example to implement a tracer middleware yourself to get the metrics you want.
    h.Use(hertztracer.ServerCtx())
kitex client
// InitJaeger ...
func InitJaeger(service string) io.Closer {
    cfg, _ := jaegercfg.FromEnv()/* 读取环境变量 */
    cfg.ServiceName = service
    tracer, closer, err := cfg.NewTracer(jaegercfg.Logger(jaeger.StdLogger))
    if err != nil {
        panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err))
    }
    opentracing.InitGlobalTracer(tracer)
    return closer
}