分布式链路追踪
分布式链路追踪就是将一次分布式请求还原成调用链路,将一次分布式请求的调用情况集中展示,比如在各个服务节点上的耗时、请求具体达到那台机器上、每个服务节点的请求状态等等。
能干什么
这里可以以 Jaeger 举例,它可以:
- 分布式跟踪信息传递
- 分布式事务监控
- 服务依赖性分析
- 展示跨进程调用链
- 定位问题
- 性能优化
Dapper
Dapper 是 Google 内部使用的分布式链路追踪系统,并没有开源,但是 Google 发布了一篇 《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》 论文,这篇论文讲述了分布式链路追踪的理论和 Dapper 的设计思想。
有很多链路追踪系统是基于 Dapper 论文的,例如淘宝的鹰眼、Twitter 的 Zipkin、Uber 开源的 Jaeger,分布式链路追踪标准 OpenTracing 等。
论文地址:static.googleusercontent.com/media/resea…
分布式链路追踪规范(OpenTracing)
OpenTracing 是与分布式系统无关的API和用于分布式跟踪的工具,它不仅提供了统一标准的 API,还致力于各种工具,帮助开发者或服务提供者开发程序。
OpenTracing 为标准 API 提供了接入 SDK,支持这些语言:Go, JavaScript, Java, Python, Ruby, PHP, Objective-C, C++, C#。
当然,我们也可以自行根据通讯协议,自己封装 SDK。
可以参考 OpenTracing 文档:opentracing.io/docs/
接下来我们要一点点弄清楚 OpenTracing 中的一些概念和知识点。由于 jaeger 是 OpenTracing 最好的实现,因此后面讲 Jaeger 就是 Opentracing ,不需要将两者严格区分。
开源框架比较
| zipkin | jaeger | skywalking | ||
|---|---|---|---|---|
| OpenTracing兼容 | 是 | 是 | 是 | |
| 客户端支持语言 | java,c#,go,php,python等 | java,c#,go,php,python等 | Java, .NET Core, NodeJS ,PHP,python | |
| 存储 | ES,mysql,Cassandra,内存 | ES,kafka,Cassandra,内存 | ES,H2,mysql,TIDB,sharding sphere | |
| 传输协议支持 | http,MQ | udp/http | gRPC | |
| ui丰富程度 | 低 | 中 | 中 | |
| 实现方式-代码侵入性 | 拦截请求,侵入 | 拦截请求,侵入 | 字节码注入,无侵入 | |
| 扩展性 | 高 | 高 | 中 | |
| trace查询 | 支持 | 支持 | 支持 | |
| 性能损失 | 中 | 中 | 低 | |
| 所属基金会 | CNCF | Apache | ||
| opentelemetry官方支持 | opentelemetry官方支持 |
jaeger
部署
-
Docker
docker run -d --name jaeger \ -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 \ -e COLLECTOR_OTLP_ENABLED=true \ -p 6831:6831/udp \ -p 6832:6832/udp \ -p 5778:5778 \ -p 16686:16686 \ -p 4317:4317 \ -p 4318:4318 \ -p 14250:14250 \ -p 14268:14268 \ -p 14269:14269 \ -p 9411:9411 \ jaegertracing/all-in-one:1.39all-in-one会把常用的组件都安装好,注意:这个docker镜像封装的jaeger是把数据放在内存中的,仅用于测试,正式使用需指定后端存储。
通过浏览器访问 http://localhost:16686 可进入Web UI面板
The container exposes the following ports:
Port Protocol Component Function 6831 UDP agent accept jaeger.thriftover Thrift-compact protocol (used by most SDKs)6832 UDP agent accept jaeger.thriftover Thrift-binary protocol (used by Node.js SDK)5775 UDP agent (deprecated) accept zipkin.thriftover compact Thrift protocol (used by legacy clients only)5778 HTTP agent serve configs (sampling, etc.) 16686 HTTP query serve frontend 4317 HTTP collector accept OpenTelemetry Protocol (OTLP) over gRPC, if enabled 4318 HTTP collector accept OpenTelemetry Protocol (OTLP) over HTTP, if enabled 14268 HTTP collector accept jaeger.thriftdirectly from clients14250 HTTP collector accept model.proto9411 HTTP collector Zipkin compatible endpoint (optional)
术语(Terminology)
Trace(踪迹)
trace 表示通过系统的数据或执行路径。它可以被认为是 span 的有向无环图。 一个简化的 Trace 如下: 注:不同编程语言的字段名称有所差异,gRPC 和 Restful API 的格式也有所差异。
"traceID": "790e003e22209ca4",
"spans":[...],
"processes":{...}
Span(跨度)
span 表示具有操作名称、操作开始时间和持续时间的逻辑工作单元。span可以嵌套和排序以模拟因果关系。
Span 由以下信息组成:
-
An operation name:操作名称,必有;
-
A start timestamp:开始时间戳,必有;
-
A finish timestamp:结束时间戳,必有;
-
Span Tags.:Key-Value 形式表示请求的标签(如一段span是调用redis的,而可以设置redis的标签,这样通过搜索redis关键字,我们就可以查询出所有相关的span以及trace),可选;
-
Span Logs:Key-Value 形式表示,记录简单的、结构化的日志,必须是字符串类型,可选;
-
SpanContext :跨度上下文,在不同的 span 中传递,建立关系; 表示标识跟踪中的 Span 的所有信息,并且必须传播到子 Span 并跨进程边界传播。SpanContext 包含从父 Span 传播到子 Span 的跟踪标识符和选项。
-
TraceId 是
trace的标识符。它是全球独一无二的,通过作为 16 个随机生成的字节,几乎有足够的概率。TraceId 用于将特定跟踪的所有跨度组合在一起,跨所有进程。 -
SpanId 是
span的标识符。它是全局唯一的,通过作为 8 个随机生成的字节,实际上有足够的概率。传递给子跨度时,此标识符将成为子跨度的父跨度 ID。 -
TraceFlags 表示跟踪的选项。它表示为 1 个字节(位图)。
- 采样位 - 表示是否对trace进行采样的位(掩码)。
0x1
- 采样位 - 表示是否对trace进行采样的位(掩码)。
-
Tracestate在键值对列表中携带特定于跟踪系统的上下文。Tracestate 允许不同的供应商传播其他信息,并与其旧版 Id 格式进行互操作。有关更多详细信息,请参阅此处。
-
-
References t:引用的其它 Span;
Baggage(附带数据)
Baggage 是任意用户定义的元数据(键值对),可以附加到分布式上下文并由 tracing SDKs 传播。See W3C Baggage for more information. 附加的数据,由key:value组成,通过附加数据,可以给调用链更多的描述信息,不过考虑到传输问题,附加数据应该尽可能少.
架构
Jaeger 既可以部署为一体式二进制文件,其中所有 Jaeger 后端组件都在单个进程中运行,也可以部署为可扩展的分布式系统,如下所述。有两个主要的部署选项:
有两种部署方式:
-
收集器直接写入存储
-
收集器写入kafka作为初步缓冲区(基于批处理思想,提高系统吞吐量)
组件(components)
Jaeger client libraries(弃用)
Jaeger 客户即将弃用。请使用 OpenTelemetry。
为不同语言实现了符合 OpenTracing 标准的 SDK。应用程序通过 API 写入数据,client library 把 trace 信息按照应用程序指定的采样策略传递给 jaeger-agent。
Agent(代理)(非必需)
jaeger-agent是一个监听在 UDP 端口上接收 span 数据的网络守护进程,它会将数据批量发送给 collector。它被设计成一个基础组件,部署到所有的宿主机上。Agent 将 client library 和 collector 解耦,为 client library 屏蔽了路由和发现 collector 的细节。
代理不是必需的组件。例如,当您的应用程序使用 OpenTelemetry 进行检测时,SDK 可以配置为将跟踪数据直接转发给 Jaeger 收集器。
Collector(收集器)
jaeger-collector从 SDK 或 jaeger-agent接收跟踪(trace),runs them through a processing pipeline for validation and clean-up/enrichment, and stores them in a storage backend.
Jaeger 内置了对多个存储后端的支持(请参阅部署),以及用于实现自定义存储插件的可扩展插件框架。
jaeger-collector支持的后端存储
Query(查询)
jaeger-query是一项服务,暴露了一些 APIs 用于从后端存储中检索 trace ,并且还托管了用于搜索和分析 trace 的Web UI。用于接收查询请求,然后从后端存储系统中检索 trace 并通过 UI 进行展示。jaeger-query是无状态的,可启动多个实例,把它们部署在 Nginx 这样的负载均衡后面。
Ingester(摄食者/摄取器)
jaeger-ingester是一种从 Kafka 读取 trace 并将其写入到存储后端的服务。实际上,它是支持 Kafka 作为唯一输入协议的 jaeger-collector的精简版。
ingester支持的后端存储
Data Store (后端存储)
后端存储被设计成一个可插拔的组件,支持将数据写入 Cassandra、ElasticSearch等。
分布式追踪系统发展很快,种类繁多,但核心步骤一般有三个:代码埋点,数据存储、查询展示
使用
因为官方强烈推荐使用openTelemetry,并且不再对jaeger-client进行维护,所以请看openTelemetry相关内容。博客跳转地址