jeager 是一个 分布式追踪工具
,在微服务架构中运用尤为广泛,
在 微服务架构 中 通常有 几个上百 个 微服务,一个完整的业务流程 通常
由 多个微服务 来 协同完成, 即便是 相同的 业务流程, 不同的请求 所
经过的 微服务 也不尽相同,这就给 错误分析 和 性能瓶颈分析 带来了 大困难
。
分部署追踪 是 解决上述问题 的常见做法,分布式追踪 是一个比较大的话题,
所涉及的 规范
、api
、第三方工具
也比较多,目前 比较主流的是 opentracing
规范。
jaeger(Go开发) 和 zipkin(Java开发) 是目前流行的 开源实现。
jaeger 提供了 对 opentracing 的支持,确保了与其他工具的兼容性。
Agent
agent 的作用是 通过 udp 接收 追踪信息,并以批量的方式 发送给 collector。
Collector
collector 负责接收 agent 发送的信息 并 进行处理, 在完成 验证、索引、转换 后 保存在 存储中。
jaeger的存储 是可插拔的 组件。目前提供了 对 kafka、elasticsearch、cassandra 的支持。
Query
query 对外提供 查询服务 和 用户界面。
Ingester
负责从 kafka 中的 topic 读取数据,并写入到 其他 存储中。
这些组件可以 统一部署 也可以 分开部署。
- 统一部署
适用于 数据量 较小的情况, 这种部署 使用的是 all-in-one 的镜像,
docker pull joinsunsoft/etcdv3-browser:1.0.0- 分开部署
每个组件 独立运行,适用于 数据量 较大的情况
Opentracing是 分布式链路追踪 的一种行为规范标准,该项目隶属于CNCF(云原生计算基金会)。
与一般的规范标准不同,它不是传输协议,消息格式层面 上的规范标准,而是一种语言层面上的API标准。
以Go语言为例,只要 某链路追踪系统 实现了Opentracing规定的接口(interface),那么就可以说该应用符合Opentracing标准。
下面介绍 Opentracing 的 两个核心概念:
Span是 一条追踪链路 中的 基本组成单位,一个span表示一个独立的工作单元,
比如可以表示一次函数调用,一次http请求等等。
span会记录如下基本信息:
- 操作名称(operation name)
- 开始时间
- 执行时长
- Tags
- Logs
- SpanContext
- References
Tags 以 键值对 的形式 保存 用户自定义标签,主要用于 链路追踪结果 的查询筛选。
其中key值必须为字符串,value必须是字符串,布尔型或者数值型。
span 中的 tag 仅自己可见,不会随着 SpanContext传递给后续span。
span.SetTag("name","tom")
logs 也是 键值对 形式 存储信息。与tags不同的是,logs还会记录写入logs的时间。
因此logs主要用于记录 某些事件发生 的时间。
logs的key值同样必须为字符串,但对value类型则没有限制。
span.LogFields(
log.String("event_type", "unkonw error happen"),
log.Int("time_out", 100),
)
SpanContext
SpanContext携带着一些用于 跨服务通信的(跨进程)
的 数据,主要包含:span_id, trace_id。
- 其key跟value都只能是字符串格式
- Baggage items 其会随着SpanContext传递给后续所有的子span。
因此要小心谨慎的使用baggage items,因为在所有的span中传递这些K,V会带来不小的网络和CPU开销
Opentracing定义了 两种引用关系 : ChildOf
和 FollowFrom
。
父span 的执行 依赖 子span的执行结果。
父span的执 不依赖 子span执行结果时。FollowFrom常用于
异步调用
的表示。
Trace表示 一个完整的追踪链路,trace由一个或多个span组成,是span的有向无环图。
[Span 1]
|
+------+------+
| |
[Span 2] [Span 3]
| |
[Span 4] +---+-------+
| |
[Span 5] [Span 6] >>> [Span 7]
3 是 1 的 `ChildOf`
7 是 6 的 `FollowsFrom`
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 9411:9411 jaegertracing/all-in-one:latest
package main
import (
"context"
"errors"
"fmt"
"io"
"time"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/log"
"github.com/uber/jaeger-client-go"
jaegercfg "github.com/uber/jaeger-client-go/config"
)
// 将jaeger tracer设置为全局tracer
func initJaeger(serviceName string) io.Closer {
cfg := jaegercfg.Configuration{
// 将采样频率设置为1,每一个span都记录,方便查看测试结果
Sampler: &jaegercfg.SamplerConfig{
Type: jaeger.SamplerTypeConst,
Param: 1,
},
Reporter: &jaegercfg.ReporterConfig{
LogSpans: true,
// 将span发往 jaeger-collector 的服务地址
CollectorEndpoint: "http://localhost:14268/api/traces",
},
}
closer, err := cfg.InitGlobalTracer(serviceName, jaegercfg.Logger(jaeger.StdLogger))
if err != nil {
panic(fmt.Sprintf("ERROR: cannot init Jaeger: %v\n", err))
}
return closer
}
func main() {
closer := initJaeger("in-process")
defer closer.Close()
// 获取jaeger tracer
t := opentracing.GlobalTracer()
// 创建root span
spanRoot := t.StartSpan("in-process-service")
// main执行完结束这个span
defer spanRoot.Finish()
// 将span传递给Foo
c := opentracing.ContextWithSpan(context.Background(), spanRoot)
// 后继传递,将context传递给Foo
Foo(c)
}
func Foo(c context.Context) {
// 开始一个span, 设置span的operation_name=Foo
span, c := opentracing.StartSpanFromContext(ctx, "Foo")
defer span.Finish()
// 后继传递,将context传递给Bar
Wtt(c)
// 模拟执行耗时
time.Sleep(1 * time.Second)
// 后继传递,将context传递给Bar
Bar(c)
}
func Bar(c context.Context) {
// 开始一个span,设置span的 operation_name = Bar
span, _ := opentracing.StartSpanFromContext(c, "Bar")
defer span.Finish()
// 模拟执行耗时
time.Sleep(2 * time.Second)
// 假设Bar发生了某些错误
err := errors.New("something wrong")
// 记录错误信息
span.LogFields(
log.String("event", "error"),
log.String("message", err.Error()),
)
span.SetTag("error", true)
}
// 内部代码 同 Bar
func Wtt(c context.Context) {
// 开始一个span,设置span的 operation_name = Bar
span, _ := opentracing.StartSpanFromContext(c, "Wtt")
defer span.Finish()
// 模拟执行耗时
time.Sleep(2 * time.Second)
// 假设Bar发生了某些错误
err := errors.New("something wrong")
// 记录错误信息
span.LogFields(
log.String("event", "error"),
log.String("message", err.Error()),
)
span.SetTag("error", true)
}