在微服务为我们提供了模块分,低耦合的高效开发和DevOPS中,具体业务中当一个请求中,请求了多个服务单元,如果请求出现了错误或异常,很难去定位是哪个服务出了问题,这时就需要链路追踪。可能你会想在业务系统中请求中埋点,或写日志,但是这种都需要在业务代码中来写,而且耦合在代码中,不具备微服务的扩张性后后期的易维护行。
链路跟踪系统可以做很多事情,如下:
其中最主要的功能就是,可以帮助开发者快速分析和诊断分布式应用架构下的性能瓶颈,提高微服务时代下的开发诊断效率。
官网 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/
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包含以下的状态:
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表示Jaeger中的逻辑工作单元,具有操作名称,操作的开始时间和持续时间。跨度可以嵌套并排序以建立因果关系模型。
在广义上,一个trace代表了一个事务或者流程在(分布式)系统中的执行过程。在OpenTracing标准中,trace是多个span组成的一个有向无环图(DAG),每一个span代表trace中被命名并计时的连续性的执行片段。
跟踪是通过系统的数据/执行路径,可以看作跨度的有向无环图。
每个 Span 包含以下对象:
Span 共有属性:
Span 可选属性:
支持 OpenTracing 的开源产品可以在这里找到 : http://opentracing.io/documentation/pages/supported-tracers.html
典型的如 SpringCloud 集成的 Zipkin 。Jaeger 是 Uber 开发的一款调用链服务端产品,开发语言为 golang ,能够兼容接收 OpenTracing 格式的数据。根据其 发展历史 ,可以说是 Zipkin 的升级版。另外,其基于 udp (也可以 http )的传输协议,更加定位了其高效、迅速的特点。
因此,后面我们重点介绍 jaeger。
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 使用介绍
参考URL: https://www.colabug.com/2017/1031/1788712/
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有5个模块元素,如下所列出,接下来我们来分别解释这五个模块的作用:
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容器化部署的基本流程: 分为测试环境和正式环境两种方式。
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,对业务是完全透明的。
官方文档: https://www.jaegertracing.io/docs/1.18/getting-started/
参考URL: https://www.cnblogs.com/battlescars/p/jaeger_deployment.html
推荐官方文档,已经比较详细!
docker pull jaegertracing/all-in-one
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界面。
解决办法:
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
成功解决!!
集成 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的工厂类
官网: 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,我们