opentracing research
tracing、 metrics、 log 构成了分布式系统可观测性的三大指标。
目前 tracing 领域最出名的项目无非是 CNCF opentracing。 其本质上是一套 tracing 设计的 API, 可以接入实现了这套 API 的 tracing 后端,如同为 CNCF 项目的 jaeger.
本篇文章分为四个部分,分别说明从 是什么,为什么,怎么用和最佳实践的角度进行介绍 opentracing 项目。
[toc]
opentracing 解决了什么问题
tracing / logging / metric 的关系
- tracing 提供了一次 request 过程中所有服务调用的全局视野
- logging 记录一些零散的、重要的日志信息
- metrics 记录了一些可以聚合的数据信息,如服务调用时长,一段时间内的调用次数等
分布式 tracing 很重要。其相较于 logging 和 metric 的局部视野,tracing 提供了一个服务调用的全局视野。
但 tracing 是侵入代码的,各家都有独特的 tracing 工具, 但不同的工具的 埋点方式不同,切换需要大量修改代码。
open tracing 统一了 tracing 的 api, 使用这套 api 可以在多个 tracing 系统中进行切换。
支持 opentracing api 的 tracer 列表
opentracing 使用场景
核心概念
Span
SpanContext
Carrier (Inject / Extract)
针对跨进程、跨主机的服务调用,直接传递 SpanContext 是不可能的,那么如何在这种情况下进行 tracing 呢?Inject / Extract 就是官方提供的一种方式,其可以将 Span 的必要信息注入到 TextMap/Http Header/Binary 中,以便传递到下游服务中。
其本质上就是将 Span 的 id 组织成 string, 然后用 Header/Binary或者其他任何方式传递给下游服务中,由下游服务提取 Span 信息,并记录父子关系,最后发送给 tracer 服务。
如何使用
python 子进程使用 opentracing
- 在每个进程中都必须初始化自己的 tracer, 如果 tracer service name 相同,则发送到同一个 service 下
- 进程间 tracing 的关系传递需要传递 span context
- open tracing 只会自添加 process 信息(包括 hostname/ ip / language + version),支持多语言调用关系
- (ps)python 起子进程会出现问题 tracer init 会出现问题,需要
Go opentracing 在进程间 tracing
使用 opentracing 的 Inject / Extract 方法提取出 carrier, 然后将carrier 传递给下游服务。上下游服务代码如下所示:
上游服务代码
1 | func GetCarrier(span opentracing.Span) string { |
下游服务代码
1 | func extractSCFromCarrier(carrier string) opentracing.SpanContext { |
具体的实验代码,见 github go socket tracing.
一些最佳实践
在 Trace 的起始处,将 Trace ID 设置为 Request ID,这么一来就打通了日志系统和分布式追踪系统,可以使用同一个 ID 查询请求的事件流和日志流,从此开启了上帝视角。
- grpc server 类中可以使用 span id 作为 request id 串联整个系统的调用路径
- client 部分也可以添加 tracing 以使得各个调用路径完整展现
- tracing 涉及到保存信息 + 发送到 agent 或 tracing server,一定会减弱性能,但可以通过设置 采样率/运行时关闭等设置避免(具体性能消耗没有相关的实验环境,暂时没有进行)