链路追踪-调用链跟踪-Jaeger

文章目录

    • 一、什么是链路跟踪
    • 二、OpenCensus
      • OpenCensus 主要特点
      • OpenTracing标准
      • 基本概念
        • Span
    • 三、典型服务端产品
      • 什么是OpenTracing?
      • opentracing 使用介绍
    • 四、Jaeger
      • Jaeger 包含的模块
      • Jaeger-client(客户端库)
    • 五、Jaeger服务容器化部署
      • 过程问题整理
    • 六、Jaeger客户端 java代码测试验证
    • 七、Jaeger客户端 go代码测试验证

一、什么是链路跟踪

在微服务为我们提供了模块分,低耦合的高效开发和DevOPS中,具体业务中当一个请求中,请求了多个服务单元,如果请求出现了错误或异常,很难去定位是哪个服务出了问题,这时就需要链路追踪。可能你会想在业务系统中请求中埋点,或写日志,但是这种都需要在业务代码中来写,而且耦合在代码中,不具备微服务的扩张性后后期的易维护行。

链路跟踪系统可以做很多事情,如下:

  • 故障快速定位
  • 各个调用环节的性能分析
  • 数据分析
  • 生成服务调用拓扑图
  • 调用请求量统计
  • 应用依赖分析

其中最主要的功能就是,可以帮助开发者快速分析和诊断分布式应用架构下的性能瓶颈,提高微服务时代下的开发诊断效率。

二、OpenCensus

官网 http://opencensus.io/
DevOps 漫谈:基于OpenCensus构建分布式跟踪系统
参考URL: https://www.jianshu.com/p/d6a2f124c766

由于各种分布式追踪系统层出不穷,且有着相似的API语法,但各种语言的开发人员依然很难将他们各自的系统和特定的分布式追踪系统进行整合。在这种情况下,OpenTracing规范出现了。

OpenCensus 是 Google 开源的一个用来收集和追踪应用程序指标中立厂商的第三方库。支持多个语言的lib库,但是不能展示。

**这个只是个数据收集的lib库,但是真正的数据展示要放到web系统当中。
其中 Zipkin, Prometheus, Jaeger 都可以展示这些数据。**其中 Prometheus Jaeger 是golang 写的。

OpenTracing通过提供平台无关、厂商无关的API,使得开发人员能够方便的添加(或更换)追踪系统的实现。OpenTracing正在为全球的分布式追踪,提供统一的概念和数据标准。

OpenTracing 只是一种协议,是一种标准化。没有它,你的应用程序照样会玩的很好,只不过失去了和其他组件交流的能力。

每一次技术迭代都是对过去技术的抽象和升华。 OpenTracing 同样抽象出调用链必须的数据模型,只需要按照其标准,一个golang语言的程序和java语言的跨平台调用链就可以很容易的实现。

官网: http://opentracing.io/

OpenCensus 主要特点

  • 标准通信协议和一致的 API :用于处理 metric 和 trace
  • 多语言库,包括Java,C++,Go,.Net,Python,PHP,Node.js,Erlang 和 Ruby
  • 与 RPC 框架的集成,可以提供开箱即用的追踪和指标。
  • 集成的存储和分析工具
  • 完全开源,支持第三方集成和输出的插件化
  • 不需要额外的服务器或守护进程来支持 OpenCensus
  • In process debugging:一个可选的代理程序,用于在目标主机上显示请求和指标数据

OpenTracing标准

OpenTracing语义标准规范及实现
参考URL: https://blog.csdn.net/u013970991/article/details/77822060
分布式追踪系统 – Opentracing
参考URL: https://zhuanlan.zhihu.com/p/83654617
Tracing与Metrics的邂逅,提供更强大的APM能力
参考URL: https://www.jianshu.com/p/9824a9eee491

OpenTracing标准 描述的语言无关的数据模型,以及OpenTracing API的使用方法。在此数据模型中,包含了两个相关的概念 Span Tag 和 (结构化的) Log Field,尽管在标准中,已经明确了这些操作,但没有定义Span的tag和logging操作时,key的使用规范。

OpenTracing数据模型
OpenTracing中的Trace(调用链)通过归属于此调用链的Span来隐性的定义。
特别说明,一条Trace(调用链)可以被认为是一个由多个Span组成的有向无环图(DAG图), Span与Span的关系被命名为References。

Span,可以被翻译为跨度,可以被理解为一次方法调用, 一个程序块的调用, 或者一次RPC/数据库访问.只要是一个具有完整时间周期的程序访问,都可以被认为是一个span.

每个Span包含以下的状态:

  • An operation name,操作名称
  • A start timestamp,起始时间
  • A finish timestamp,结束时间
  • Span Tag,一组键值对构成的Span标签集合。键值对中,键必须为string,值可以是字符串,布尔,或者数字类型。
  • Span Log,一组span的日志集合。
    每次log操作包含一个键值对,以及一个时间戳。
    键值对中,键必须为string,值可以是任意类型。
    但是需要注意,不是所有的支持OpenTracing的Tracer,都需要支持所有的值类型。
  • SpanContext,Span上下文对象 (下面会详细说明)
  • References(Span间关系),相关的零个或者多个Span(Span间通过SpanContext建立这种关系)

OpenTracing API

  • Tracer
    Tracer接口用来创建Span,以及处理如何处理Inject(serialize) 和 Extract (deserialize),用于跨进程边界传递。它具有如下官方能力:
    创建一个新Span
    必填参数
    operation name, 操作名, 一个具有可读性的字符串,代表这个span所做的工作(例如:RPC方法名,方法名,或者一个大型计算中的某个阶段或子任务)。操作名应该是一个抽象、通用,明确、具有统计意义的名称。因此,“get_user” 作为操作名,比 "get_user/314159"更好。
    例如,假设一个获取账户信息的span会有如下可能的名称:

    操作名 指导意见
    get 太抽象
    get_account/792 太明确
    get_account 正确的操作名,关于account_id=792的信息应该使用Tag操作

  • Span
    当Span结束后(span.finish()),除了通过Span获取SpanContext外,下列其他所有方法都不允许被调用。
    不需要任何参数。

返回值,Span构建时传入的SpanContext。这个返回值在Span结束后(span.finish()),依然可以使用。

基本概念

Span

Span表示Jaeger中的逻辑工作单元,具有操作名称,操作的开始时间和持续时间。跨度可以嵌套并排序以建立因果关系模型。

在广义上,一个trace代表了一个事务或者流程在(分布式)系统中的执行过程。在OpenTracing标准中,trace是多个span组成的一个有向无环图(DAG),每一个span代表trace中被命名并计时的连续性的执行片段。

跟踪是通过系统的数据/执行路径,可以看作跨度的有向无环图。
链路追踪-调用链跟踪-Jaeger_第1张图片每个 Span 包含以下对象:

  • Operation name:操作名称 (也可以称作 Span name)。
  • Start timestamp:起始时间。
  • Finish timestamp:结束时间。
  • Span tag:一组键值对构成的 Span 标签集合。键值对中,键必须为 String,值可以是字符串、布尔或者数字类型。
  • Span log:一组 Span 的日志集合。每次 Log 操作包含一个键值对和一个时间戳。键值对中,键必须为 String,值可以是任意类型。
  • SpanContext: pan 上下文对象。每个 SpanContext 包含以下状态:
    • 要实现任何一个 OpenTracing,都需要依赖一个独特的 Span 去跨进程边界传输当前调用链的状态(例如:Trace 和 Span 的 ID)。
    • Baggage Items 是 Trace 的随行数据,是一个键值对集合,存在于 Trace 中,也需要跨进程边界传输。
  • References(Span 间关系):相关的零个或者多个 Span(Span 间通过 SpanContext 建立这种关系)。

Span 共有属性:

  • TraceId
  • SpanId
  • Start Time
  • End Time
  • Status

Span 可选属性:

  • Parent SpanId
  • Remote Parent
  • Attributes
  • Annotations
  • Message Events
  • Links

三、典型服务端产品

支持 OpenTracing 的开源产品可以在这里找到 : http://opentracing.io/documentation/pages/supported-tracers.html

典型的如 SpringCloud 集成的 Zipkin 。Jaeger 是 Uber 开发的一款调用链服务端产品,开发语言为 golang ,能够兼容接收 OpenTracing 格式的数据。根据其 发展历史 ,可以说是 Zipkin 的升级版。另外,其基于 udp (也可以 http )的传输协议,更加定位了其高效、迅速的特点。

因此,后面我们重点介绍 jaeger。

什么是OpenTracing?

OpenTracing语义标准规范及实现
参考URL: https://blog.csdn.net/u013970991/article/details/77822060
Go微服务全链路跟踪详解
参考URL: https://studygolang.com/articles/23528?fr=sidebar

现在有许多开源的分布式跟踪库可供选择,其中最受欢迎的库可能是Zipkin²和Jaeger³。 选择哪个是一个令人头疼的问题,因为你现在可以选择最受欢迎的一个,但是如果以后有一个更好的出现呢?OpenTracing⁴可以帮你解决这个问题。它建立了一套跟踪库的通用接口,这样你的程序只需要调用这些接口而不被具体的跟踪库绑定,将来可以切换到不同的跟踪库而无需更改代码。Zipkin和Jaeger都支持OpenTracing。

OpenTracing通过提供平台无关、厂商无关的API,使得开发人员能够方便的添加(或更换)追踪系统的实现。OpenTracing正在为全球的分布式追踪,提供统一的概念和数据标准。

关于OpenTracing的一个Java版本的实现请参考如下代码:
https://github.com/opentracing/opentracing-java

opentracing 使用介绍

opentracing 使用介绍
参考URL: https://www.colabug.com/2017/1031/1788712/

四、Jaeger

Jaeger-分布式调用链跟踪系统理论与实战
参考URL: https://blog.csdn.net/zuiyijiangnan/article/details/103836060
对接Jaeger
参考URL: https://help.aliyun.com/document_detail/68035.html

Jaeger 是 Uber 开发的一款调用链服务端产品,开发语言为 golang ,能够兼容接收 OpenTracing 格式的数据。根据其 发展历史 ,可以说是 Zipkin 的升级版。另外,其基于 udp (也可以 http )的传输协议,更加定位了其高效、迅速的特点

Jaeger 开源社区
作为一个开源项目,一个由数百个贡献者组成的社区不断改进完善着 Jaeger。Jaeger 基于与供应商无关的 OpenTracing API 和工具。

共享出行公司 Uber 在 2015 年开发了开源项目 Jaeger。2017 年,Jaeger 纳入云原生计算基金会(CNCF)的孵化项目,2019 年,Jaeger 正式毕业。

Jaeger 包含的模块

Jaeger有5个模块元素,如下所列出,接下来我们来分别解释这五个模块的作用:

  • Jaeger-client
    (OpenTracing API 各语言的实现,用于在应用中塞入信息采集点)

jaeger-client直接采集服务站点的链路数据,统一发送到Kafka消息队列,Kafka消费端go-server直接对数据进行消费,存储Cassandra。

  • Agent
    负责发送的进程,对 spans 进行处理并发送给 collector,监听 spans 的 UDP 发送。设计这层是为了作为基础组件部署到主机上,从 client 中抽象出了 collector 的发现和路由

  • Collector
    接收agent 批量发来的数据,将数据落地到我们选择的存储里面,Collector 本身是无状态的。我们可以运行多个collector.

  • Data Store
    追踪信息的存储Cassandra

  • jaeger-query
    从存储中检索追踪信息并通过 UI 展示。

  • jaeger-ui
    UI 展示层,基于 React。 顾名思义,数据我们既然存储落地了,这就是个查询展示的页面。

一套完整的Jager追踪系统包括Jaeger-client、Jaeger-agent、Jaeger-collector、Database和Jaeger-query UI等基本组件,如下图架构图所示,**Jaeger客户端支持多种语言,jaeger-agent与客户端进行数据交互,并把数据push到Jaeger-collector组件,Jaeger-collector将数据持久化到数据库,Jaeger-query是一个web服务,用于展示跟踪链路。**以下为Jaeger容器化部署的基本流程: 分为测试环境和正式环境两种方式。
链路追踪-调用链跟踪-Jaeger_第2张图片

Jaeger-client(客户端库)

client是客户端lib,便于不同语言的项目来介入到Jaeger中,当我们的应用程序装载上之后,client会负责收集并发送数据到Agent。当前Jaeger的SDK支持有如下:

–官方
1.Go
2.Java
3.Node
4.Python
5.C++
–非官方
1.PHP
3.Others

client是支持OpenTracing标准的,如上文提到,Zipkin也是支持OpenTracing标准的,这也就意味着,我们的应用程序在嵌入Jaeger-client的地方,可以随时替换成Zipkin,对业务是完全透明的。

五、Jaeger服务容器化部署

官方文档: https://www.jaegertracing.io/docs/1.18/getting-started/
参考URL: https://www.cnblogs.com/battlescars/p/jaeger_deployment.html

推荐官方文档,已经比较详细!

  1. 下载jaeger docker 镜像。
docker pull jaegertracing/all-in-one
  1. 运行docker镜像
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

网上demo,应该是只需要把 6831、16686端口暴露出来,未测试

docker run -d --name jaeger \
  -p 6831:6831/udp \
  -p 16686:16686 \
  jaegertracing/all-in-one:1.17

现在可以访问localhost:16686来查看jaeger的UI界面。

过程问题整理

  1. docker run运行 jaeger 报错:WARNING: IPv4 forwarding is disabled. Networking will not work.

解决办法:

vi /etc/sysctl.conf
或者

vi /usr/lib/sysctl.d/00-system.conf
添加如下代码:
net.ipv4.ip_forward=1

重启network服务

systemctl restart network
查看是否修改成功

sysctl net.ipv4.ip_forward
如果返回为“net.ipv4.ip_forward = 1”则表示成功了

解决办法:

在宿主机上面执行:

net.ipv4.ip_forward=1 >> /usr/lib/sysctl.d/00-system.conf
重启network和docker服务

systemctl restart network && systemctl restart docker
成功解决!!

六、Jaeger客户端 java代码测试验证

集成 opencensus
https://opencensus.io/guides/exporters/supported-exporters/java/jaeger/

关于OpenTracing的一个Java版本的实现请参考如下代码:https://github.com/opentracing/opentracing-java

Tracing中依赖的几个重要类

Endpoint - IP,端口和应用服务名等信息
Sampler - 采样器,根据traceId来判断是否一条trace需要被采样,即上报到zipkin
TraceContext - 包含TraceId,SpanId,是否采样等数据
CurrentTraceContext - 是一个辅助类,可以用于获得当前线程的TraceContext
Propagation - 是一个可以向数据携带的对象carrier上注入(inject)和提取(extract)数据的接口
Propagation.Factory - Propagation的工厂类

七、Jaeger客户端 go代码测试验证

官网: https://opencensus.io/guides/grpc/go/
golang实现简单的jaeger-demo
参考URL: https://www.pianshen.com/article/6298261661/
代码示例可参考一位go大佬的一个示例:https://github.com/xinliangnote/go-gin-api

调私有方法setupJaegerTracing,设置 tracing

	jaeger := setupJaegerTracing("lotus")

私有方法setupJaegerTracing 设置了 jaeger agent地址。

整体思路为:
jaeger.NewExporter 方法创建一个 NewExporter ,然后调opencensus的 trace.RegisterExporter 注册这个输出器,这样就把 jaeger和trace关联起来了。 然后就可以调用 trace.StartSpan 开始一个Span。

demo代码如下:

package main

import (
	"context"
	"fmt"
	"github.com/urfave/cli/v2"
	"go.opencensus.io/trace"
	"os"
	"time"


	"contrib.go.opencensus.io/exporter/jaeger"
	logging "github.com/ipfs/go-log/v2"
)

var log = logging.Logger("tracing")


func main() {
	logging.SetLogLevel("*", "info")

	app := &cli.App{
		Name: "greet",
		Usage: "say a greeting",
		Action: func(c *cli.Context) error {

			fmt.Println("Greetings")
			//todo some thing

			return nil
		},
	}


	jaeger := setupJaegerTracing("lotus")
	defer func() {
		if jaeger != nil {
			jaeger.Flush()
		}
	}()


	ctx, span := trace.StartSpan(context.Background(), "/cli")
	defer span.End()

	fmt.Println("Hello world!")


	duration := time.Duration(10) * time.Second
	time.Sleep(duration)
	fmt.Println("Hello world!2")



	app.Setup()
	app.Metadata["traceContext"] = ctx
	if err := app.Run(os.Args); err != nil {
		span.SetStatus(trace.Status{
			Code:    trace.StatusCodeFailedPrecondition,
			Message: err.Error(),
		})
		os.Exit(1)
	}

}


func setupJaegerTracing(serviceName string) *jaeger.Exporter {

	//if _, ok := os.LookupEnv("LOTUS_JAEGER"); !ok {
	//	return nil
	//}
	//agentEndpointURI := os.Getenv("LOTUS_JAEGER")
	agentEndpointURI := "127.0.0.1:6831"

	je, err := jaeger.NewExporter(jaeger.Options{
		AgentEndpoint: agentEndpointURI,
		ServiceName:   serviceName,
	})
	if err != nil {
		log.Errorw("Failed to create the Jaeger exporter", "error", err)
		return nil
	}

	trace.RegisterExporter(je)
	trace.ApplyConfig(trace.Config{
		DefaultSampler: trace.AlwaysSample(),
	})
	return je
}

登录 127.0.0.1:16686 查看结果,可以看到 我们在 StartSpan指定的 操作名 cli,我们

链路追踪-调用链跟踪-Jaeger_第3张图片

你可能感兴趣的:(Java后台,Go,java,分布式,golang)