一、OpenTelemetry
分布式链路跟踪( Distributed Tracing
)的概念最早是由 Google
提出来的,发展至今技术已经比较成熟,也是有一些协议标准可以参考。目前在 Tracing
技术这块比较有影响力的是两大开源技术框架: Netflix
公司开源的 OpenTracing
和 Google
开源的 OpenCensus
。两大框架都拥有比较高的开发者群体。为形成统一的技术标准,两大 框架最终磨合成立了 OpenTelemetry
项目,简称 otel
。具体可以参考:
因此,我们的 Tracing
技术方案以 OpenTelemetry
为实施标准,协议标准的一些 Golang
实现开源项目:
- https://github.com/open-telemetry/opentelemetry-go
- https://github.com/open-telemetry/opentelemetry-go-contrib
其他第三方的框架和系统(如 Jaeger/Prometheus/Grafana
等)也会按照标准化的规范来对接 OpenTelemetry
,使得系统的开发和维护成本大大降低。
二、重要概念
我们先看看 OpenTelemetry
的架构图,我们这里不会完整介绍,只会介绍其中大家常用的几个概念。关于 OpenTelemetry
的内部技术架构设计介绍,可以参考 OpenTelemetry架构 ,关于语义约定请参考: https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md
TracerProvider
主要负责创建 Tracer
,一般是需要第三方的分布式链路跟踪管理平台提供具体的实现。默认情况是一个空的 TracerProvider (NoopTracerProvider)
,虽然也能创建 Tracer
但是内部其实不会执行具体的数据流传输逻辑。
Tracer
Tracer
表示一次完整的追踪链路, tracer
由一个或多个 span
组成。下图示例表示了一个由 8
个 span
组成的 tracer
:
[Span A] ←←←(the root span)
|
+------+------+
| |
[Span B] [Span C] ←←←(Span C is a `ChildOf` Span A)
| |
[Span D] +---+-------+
| |
[Span E] [Span F] >>> [Span G] >>> [Span H]
↑
↑
↑
(Span G `FollowsFrom` Span F)
时间轴的展现方式会更容易理解:
––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–––––––|–> time
[Span A···················································]
[Span B··············································]
[Span D··········································]
[Span C········································]
[Span E·······] [Span F··] [Span G··] [Span H··]
我们通常通过以下方式创建一个 Tracer
:
gtrace.NewTracer(tracerName)
Span
Span
是一条追踪链路中的基本组成要素,一个 span
表示一个独立的工作单元,比如可以表示一次函数调用,一次 http
请求等等。 span
会记录如下基本要素:
- 服务名称(
operation name
) - 服务的开始时间和结束时间
K/V
形式的Tags
K/V
形式的Logs
SpanContext
Span
是这么多对象中使用频率最高的,因此创建 Span
也非常简便,例如:
gtrace.NewSpan(ctx, spanName, opts...)
Attributes
Attributes
以 K/V
键值对的形式保存用户自定义标签,主要用于链路追踪结果的查询过滤。例如: http.method="GET",http.status_code=200
。其中 key
值必须为字符串, value
必须是字符串,布尔型或者数值型。 span
中的 Attributes
仅自己可见,不会随着 SpanContext
传递给后续 span
。 设置 Attributes
方式例如:
span.SetAttributes(
label.String("http.remote", conn.RemoteAddr().String()),
label.String("http.local", conn.LocalAddr().String()),
)