使用jaeger作为trace工具生成服务trace图(服务依赖)

2 篇文章 0 订阅
2 篇文章 0 订阅

jaeger安装与测试

github地址

jaeger简介

参考文档

Jaeger 受 Dapper 和 OpenZipkin 的启发,是由 Uber Technologies 发布的开源分布式追踪系统。它用于监控和排查基于微服务的分布式系统问题,包括:

  • 分布式上下文传播
  • 分布式事务监控
  • 根因分析
  • 服务依赖关系分析
  • 性能 / 延迟优化

Jaeger 架构:

在这里插入图片描述

整体上讲,一个基础的 Jaeger 追踪系统包含下面几个部分:

  • jaeger-query: 用于客户端查询和检索组件,并包含了一个基础的 UI
  • jaeger-collector: 接收来自 jaeger-agent 的 trace 数据,并通过处理管道来执行。当前的处理管道包含验证 trace 数据,创建索引,执行数据转换以及将数据存储到对应的后端
  • jaeger-agent: 一个网络守护进程,侦听通过 UDP 发送的 spans ,它对其进行批处理并发送给收集器。它被设计为作为基础设施组件部署到所有主机。代理将收集器的路由和发现从客户机抽象出来
  • backend-storage: 用于指标数据存储的可插拔式后端存储,支持 Cassandra, Elasticsearch and Kafka
  • ingester: 可选组件,用于从 kafka 中消费数据并写入到可直接读取的 Cassandra 或 Elasticsearch 存储中

注意: trace 和 span 是链路追踪系统中的专业属于,span 就表示具体的一个工作单元,并且包含了该单元的元数据比如具体是什么操作,执行的时间等等,而 trace 是通过整个系统的数据路径,可以被认为是将 Spans 组成一个有向无环图 (DAG)

而对于一个多组件系统来说,也存在多个网络端口用于数据的传输,下图即为各个组件的端口用途:

在这里插入图片描述

安装

# 安装docker
apt install docker.io -y
docker run -d --name jaeger \
  -e COLLECTOR_ZIPKIN_HTTP_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:latest

访问http://10.1.1.70:16686/search查看页面

编写示例trace代码

要在 Golang 微服务中集成 Jaeger 进行分布式跟踪,您需要使用 OpenTracing API 和 Jaeger 的 Golang 客户端库。以下是三个微服务的示例,它们通过 Jaeger 进行跟踪。请注意,您需要先安装 github.com/uber/jaeger-client-gogithub.com/opentracing/opentracing-go 库。

server1 - 服务 A:

package main

import (
        "io/ioutil"
        "log"
        "net/http"
        "time"

        "github.com/opentracing/opentracing-go"
        "github.com/uber/jaeger-client-go"
        "github.com/uber/jaeger-client-go/config"
)

func main() {
        // 初始化 Jaeger tracer
        cfg := config.Configuration{
                ServiceName: "serviceA",
                Sampler: &config.SamplerConfig{
                        Type:  jaeger.SamplerTypeConst,
                        Param: 1,
                },
                Reporter: &config.ReporterConfig{
                        LogSpans:           true,
                        LocalAgentHostPort: "localhost:6831", // Jaeger Agent 的地址
                },
        }
        tracer, closer, err := cfg.NewTracer(config.Logger(jaeger.StdLogger))
        if err != nil {
                log.Fatalf("Could not initialize jaeger tracer: %s", err)
        }
        defer closer.Close()
        opentracing.SetGlobalTracer(tracer)

        // Define HTTP handler for serviceA
        http.HandleFunc("/serviceA", func(w http.ResponseWriter, r *http.Request) {
                // 开始一个新的 span
                span := tracer.StartSpan("/serviceA")
                defer span.Finish()

                // 发送HTTP请求到服务B
                req, _ := http.NewRequest("GET", "http://localhost:8082/serviceB", nil)
                // 将 span 的 context 注入到 HTTP 请求头中
                err := tracer.Inject(span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
                if err != nil {
                        log.Fatalf("Could not inject span context into header: %s", err)
                }

                resp, err := http.DefaultClient.Do(req)
                if err != nil {
                        log.Fatal(err)
                }
                defer resp.Body.Close()

                // Simulate some processing
                time.Sleep(45 * time.Millisecond)

                // 读取服务B的响应
                body, err := ioutil.ReadAll(resp.Body)
                if err != nil {
                        log.Fatal(err)
                }

                // 将服务B的响应写入服务A的响应体
                w.Write(body)
        })

        log.Fatal(http.ListenAndServe(":8081", nil))
}

server2 - 服务 B:

package main

import (
        "io/ioutil"
        "log"
        "net/http"
        "time"

        "github.com/opentracing/opentracing-go"
        "github.com/uber/jaeger-client-go"
        "github.com/uber/jaeger-client-go/config"
)

func main() {
        // 初始化 Jaeger tracer
        cfg := config.Configuration{
                ServiceName: "serviceB",
                Sampler: &config.SamplerConfig{
                        Type:  jaeger.SamplerTypeConst,
                        Param: 1,
                },
                Reporter: &config.ReporterConfig{
                        LogSpans:           true,
                        LocalAgentHostPort: "localhost:6831", // Jaeger Agent 的地址
                },
        }
        tracer, closer, err := cfg.NewTracer(config.Logger(jaeger.StdLogger))
        if err != nil {
                log.Fatalf("Could not initialize jaeger tracer: %s", err)
        }
        defer closer.Close()
        opentracing.SetGlobalTracer(tracer)

        // Define HTTP handler for serviceB
        http.HandleFunc("/serviceB", func(w http.ResponseWriter, r *http.Request) {
                // 从 HTTP 请求头中提取父 span 的 context
                parentSpanContext, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))
                // 创建一个新的 span 作为父 span 的子 span
                span := tracer.StartSpan("/serviceB", opentracing.ChildOf(parentSpanContext))
                defer span.Finish()

                // 发送HTTP请求到服务B
                req, _ := http.NewRequest("GET", "http://localhost:8083/serviceC", nil)
                // 将 span 的 context 注入到 HTTP 请求头中
                err := tracer.Inject(span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
                if err != nil {
                        log.Fatalf("Could not inject span context into header: %s", err)
                }

                resp, err := http.DefaultClient.Do(req)
                if err != nil {
                        log.Fatal(err)
                }
                defer resp.Body.Close()

                // Simulate some processing
                time.Sleep(45 * time.Millisecond)

                // 读取服务B的响应
                body, err := ioutil.ReadAll(resp.Body)
                if err != nil {
                        log.Fatal(err)
                }

                // 将服务B的响应写入服务A的响应体
                w.Write(body)
        })

        log.Fatal(http.ListenAndServe(":8082", nil))
}

server3 - 服务 C:

package main

import (
        "io/ioutil"
        "log"
        "net/http"
        "time"

        "github.com/opentracing/opentracing-go"
        "github.com/uber/jaeger-client-go"
        "github.com/uber/jaeger-client-go/config"
)

func main() {
        // 初始化 Jaeger tracer
        cfg := config.Configuration{
                ServiceName: "serviceC",
                Sampler: &config.SamplerConfig{
                        Type:  jaeger.SamplerTypeConst,
                        Param: 1,
                },
                Reporter: &config.ReporterConfig{
                        LogSpans:           true,
                        LocalAgentHostPort: "localhost:6831", // Jaeger Agent 的地址
                },
        }
        tracer, closer, err := cfg.NewTracer(config.Logger(jaeger.StdLogger))
        if err != nil {
                log.Fatalf("Could not initialize jaeger tracer: %s", err)
        }
        defer closer.Close()
        opentracing.SetGlobalTracer(tracer)

        // Define HTTP handler for serviceB
        http.HandleFunc("/serviceC", func(w http.ResponseWriter, r *http.Request) {
                // 从 HTTP 请求头中提取父 span 的 context
                parentSpanContext, _ := tracer.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(r.Header))
                // 创建一个新的 span 作为父 span 的子 span
                span := tracer.StartSpan("/serviceC", opentracing.ChildOf(parentSpanContext))
                defer span.Finish()

                // 发送HTTP请求到服务B
                req, _ := http.NewRequest("GET", "http://ifconfig.me", nil)
                // 将 span 的 context 注入到 HTTP 请求头中
                err := tracer.Inject(span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
                if err != nil {
                        log.Fatalf("Could not inject span context into header: %s", err)
                }

                resp, err := http.DefaultClient.Do(req)
                if err != nil {
                        log.Fatal(err)
                }
                defer resp.Body.Close()

                // Simulate some processing
                time.Sleep(45 * time.Millisecond)

                // 读取服务B的响应
                body, err := ioutil.ReadAll(resp.Body)
                if err != nil {
                        log.Fatal(err)
                }

                // 将服务B的响应写入服务A的响应体
                w.Write(body)
        })

        log.Fatal(http.ListenAndServe(":8083", nil))
}

在这些示例中,每个服务都会创建一个新的 span,并在发送 HTTP 请求时将 span 的 context 注入到请求头中。接收服务会从请求头中提取 span context,并创建一个新的 span 作为子 span。这样,Jaeger 就可以跟踪整个请求链路。

启动测试程序

screen -dmS srv1 /root/test/srv1 &
screen -dmS srv2 /root/test/srv2 &
screen -dmS srv3 /root/test/srv3 &

访问serviceA

curl http://localhost:8081/serviceA

查看jaeger面板

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

FAQ

jaeger trace应用同样需要插入代码吗

是的,要在应用程序中使用 Jaeger 进行跟踪,您需要在代码中插入一些代码来启动和记录跟踪信息。以下是使用 Jaeger 的一般步骤:

  1. 添加 Jaeger 客户端库

    • 根据您的编程语言和框架,添加适当的 Jaeger 客户端库。这些库允许您创建和记录跟踪数据。
    • 例如,在 Java 中,您可以使用 io.opentracing 库。
  2. 初始化 Jaeger Tracer

    • 在应用程序的启动代码中,初始化 Jaeger Tracer。这通常涉及设置连接到 Jaeger 后端的配置,例如 Jaeger Collector 的地址。
  3. 创建和记录 Spans

    • 在应用程序的关键代码路径中,创建和记录 spans。
    • 每个 span 表示一个操作或事件,例如处理 HTTP 请求、数据库查询等。
    • 您可以使用 startSpan 方法来创建 span,并使用 finish 方法来结束它。
  4. 将 Trace 数据发送到 Jaeger 后端

    • 在每个 span 结束时,将 trace 数据发送到 Jaeger 后端。
    • 这通常是异步的,以避免对应用程序性能产生太大的影响。
  5. 查看 Jaeger UI

    • 启动 Jaeger UI,您可以在其中查看跟踪数据、服务依赖关系和其他相关信息。

总之,要使用 Jaeger 进行跟踪,您需要在应用程序中插入代码来创建、记录和发送跟踪数据。这样,您就可以在 Jaeger UI 中查看分布式调用链的详细信息。🔍

jaeger会发送metrics吗

是的,Jaeger 会发送指标(metrics)。默认情况下,Jaeger 的微服务以 Prometheus 格式公开指标。这些指标可以用于监控和性能分析。以下是一些与 Jaeger 监控和指标相关的信息:

  1. Prometheus 格式的指标

    • Jaeger 的各个组件(如 jaeger-agent、jaeger-collector、jaeger-query 等)都会在管理端口上公开 Prometheus 格式的指标。
    • 您可以通过访问 /metrics 路径来获取这些指标。
  2. Jaeger 的监控 mixin

    • Jaeger 提供了一个 Prometheus 监控 mixin,用于帮助人们使用 Prometheus、Alertmanager 和 Grafana 监控 Jaeger。
    • 这包括一个预构建的仪表盘,用于可视化 Jaeger 的性能指标。
  3. Jaeger 的日志

    • Jaeger 组件只记录到标准输出,使用结构化日志库 go.uber.org/zap 配置为将日志行写入 JSON 编码的字符串。

总之,Jaeger 会生成 Prometheus 格式的指标,您可以使用 Prometheus、Grafana 等工具来监控和分析这些指标。🔍

如果您想深入了解 Jaeger 的监控和指标,可以查阅 Jaeger 的官方文档。

  • 9
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值