阅读(2513) (11)

GoFrame 链路跟踪-背景知识

2022-04-01 14:13:21 更新

一、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()),
)
    

Events

Events​与​Attributes​类似,也是​K/V​键值对形式。与​Attributes​不同的是,​Events​还会记录写入​Events​的时间,因此​Events​主要用于记录某些事件发生的时间。​Events​的​key​值同样必须为字符串,但对​value​类型则没有限制。例如:

span.AddEvent("http.request", trace.WithAttributes(
	label.Any("http.request.header", headers),
	label.Any("http.request.baggage", gtrace.GetBaggageMap(ctx)),
	label.String("http.request.body", bodyContent),
))
        

SpanContext

SpanContext​携带着一些用于跨服务通信的(跨进程)数据,主要包含:

  • 足够在系统中标识该​span​的信息,比如:​span_id​, ​trace_id​。
  • Baggage​-为整条追踪连保存跨服务(跨进程)的​K/V​格式的用户自定义数据。​Baggage​ 与 ​Attributes​ 类似,也是 ​K/V​ 键值对。与Attributes不同的是:
    • 其​key​跟​value​都只能是字符串格式
    • Baggage​不仅当前​span​可见,其会随着​SpanContext​传递给后续所有的子​span​。要小心谨慎的使用​Baggage- 因为在所有的​span​中传递这些​K,V​会带来不小的网络和​CPU​开销。

Propagator

Propagator​传播器用于端对端的数据编码/解码,例如:​Client​到​Server​端的数据传输,​TraceId​、​SpanId​和​Baggage​也是需要通过传播器来管理数据传输。业务端开发者往往对​Propagator​无感知,只有中间件/拦截器的开发者需要知道它的作用。​OpenTelemetry​的标准协议实现库提供了常用的​TextMapPropagator​,用于常见的文本数据端到端传输。此外,为保证​TextMapPropagator​中的传输数据兼容性,不应当带有特殊字符,具体请参考:https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/context/api-propagators.md

GoFrame​框架通过​gtrace 模块使用了以下传播器对象,并全局设置到了​OpenTelemetry​中:

// defaultTextMapPropagator is the default propagator for context propagation between peers.
defaultTextMapPropagator = propagation.NewCompositeTextMapPropagator(
	propagation.TraceContext{},
	propagation.Baggage{},
)

三、支持组件

GoFrame​的核心组件都已经全面支持​OpenTelemetry​标准,并且自动开启了链路跟踪特性,开发者无需显示调用、使用无感知。在没有注入外部​TracerProvider​的情况下,框架会使用默认的​TracerProvider​,该​TracerProvider​只会自动创建​TraceID​及​SpanID​,并不会执行复杂逻辑。

包括但不限于以下核心组件:

  • Http Client

HTTP​客户端自动启用了链路跟踪特性。

  • Http Server

HTTP​服务端自动启用了链路跟踪特性。

  • gRPC Client

gRPC​客户端自动启用了链路跟踪特性。

  • gRPC Server

gRPC​服务端自动启用了链路跟踪特性。

  • Logging

日志内容中需要注入当前请求的​TraceId​,以方便通过日志快速查找定位问题点。该特性是由​glog​组件实现,这需要开发者在输出日志的时候调用​Ctx​链式操作方法将​context.Context​上下文变量传递到当前输出日志操作链路中,没有传递​context.Context​上下文变量,就会丢失日志内容中的​TraceId​。

  • ORM

数据库的执行是很重要的链路环节,​Orm​组件需要将自身的执行情况投递到链路中,作为执行链路的一部分。

  • Redis

Redis​的执行也是很重要的链路环节,​Redis​需要将自身的执行情况投递到链路中,作为执行链路的一部分。

  • Utils

对于​Tracing​特性的管理需要做一定的封装,主要考虑的是可扩展性和易用性两方面。该封装由​gtrace​模块实现,文档地址:https://pkg.go.dev/github.com/gogf/gf/v2/net/gtrace