每一个对Inject和Extract存在疑惑,又羞于启齿的人

这篇文档,针对InjectExtract设计和用法,提供一个简要的总结,而不考虑特定的OpenTracingg规范各语言的实现和基于OpenTracing标准的追踪系统。

分布式追踪系统最困难的部分就是在分布式的应用环境下保持追踪的正常工作。任何一个追踪系统,都需要理解多个跨进程调用间的因果关系,无论他们是通过RPC框架、发布-订阅机制、通用消息队列、HTTP请求调用、UDP传输或者其他传输模式。

一些分布式追踪系统(例如,2003年的Project5,2006年的,2014年的The Mystery Machine)会推断跨进程间的因果关系。当然,这些系统,都需要在基于黑盒的因果关系推断 与 追踪结果的整合、实时准确展现上,进行处理折衷。 处于对准确展现的关注,OpenTracing是一个明确的分布式追踪系统标准,它更倾向于如果产品的处理方式:2007年的,2010年的Dapper,以及很多开源的追踪系统,如:,Appdash 等等

InjectExtract 允许开发者进行跨进程追踪时,不用和特定的OpenTracing实现进行紧耦合

OpenTracing跨进程传播需求

  • 如上文所述,OpenTracing 用户 处理跨进程的追踪传输时,必须不需要使用OpenTracing使用中的特定代码
  • 基于OpenTracing的追踪系统,必须不需要针对每一种已知的跨进程通讯机制都进行处理:这其中包含太多的工作,很多还没有明确的定义
  • 也就是说,这套传播机制必须是最利于扩展的

追踪过程中的任何一个SpanContext可以被Injected(注入)到一个Carrier中。Carrier可以是一个接口或者一个数据载体,他对于跨进程通讯(IPC)是十分有帮助的。Carrier负责将追踪状态从一个进程”carries”(携带,传递)到另一个进程。OpenTracing标准包含两种,尽管,自定义的 Carrier 格式 也是可能的。

同样的,对于一个Carrier,如果已经被Injected,那么它也可以被Extracted(提取),从而得到一个SpanContext实例。这个SpanContext代表着被Injected到Carrier的信息。

Inject伪代码示例

Extract 伪代码示例

Carrier格式

所有的Carrier都有自己的格式。在一些语言的OpenTracing实现中,格式必须必须作为一个常量或者字符串来指定; 另一些,则通过Carrier的静态类型来指定。

Inject/Extract Carrier 所必须的格式

至少,OpenTracing标准所有平台的实现者支持两种Carrier格式:基于”text map”(基于字符串的map)的格式和基于”binary”(二进制)的格式。

  • text map 格式的 Carrier是一个平台惯用的map格式,基于unicode编码的字符串字符串键值对

不能期待不同的OpenTracing实现,InjectExtract SpanContexts采用相互兼容的方式。虽然OpenTracing对于实现 跨整个分布式系统 的追踪系统是无从得知的,为了成功实现跨进程的追踪的我收过程,跨进程追踪的两端应该使用相同的追踪系统实现。(即远程调用的两段,使用同一套tracer)。

任何的基于网络传输的子系统(RPC库,消息队列等)可能选择引入他们自定义的Inject/Extract的Carrier格式;根据需要自定义格式,但最终要求返回符合OpenTracing格式的结果。这样允许OpenTracing的实现者可以优化他们自己的自定义格式,而不需要实现者支持这些子系统的自定义格式。

一些伪代码将可能更明确的说明这个问题。假设我们是ArrrPC pirate RPC subsystem的作者,我们希望增加OpenTracing的数据在RPC请求过程中传输。不考虑异常处理,我们的伪代码可能如下所示:

关于Carrier自定义格式的更多内容

“Carrier的格式”在不同平台可能是不一样的,但在所有的场景下,他们都会使用一个全局的命名空间。支持一个全新的自定义格式的carrier不必修改OpenTracing核心平台的API, 尽管每一个实现OpenTracing平台API时,必须定义符合OpenTracing标准要求的carrier格式(比如:基于字符串的map和二进制块)。例如,ArrrPC RPC的维护团队定义了一个叫做”ArrrPC”的Inject/Extract格式,他们不需要向OpenTracing团队提交PR(当然OpenTracing的实现者不要求一定支持”ArrrPC”格式)。an end-to-end injector and extractor example below,一个端到端的injector和extractor示例 将更具体的描述这个问题。

一个端到端的injector和extractor示例

  1. 一个客户端进程有一个SpanContext实例,并准备进行通过自制的HTTP协议,进行一次RPC调用
  2. 客户端进程调用Tracer.Inject(...),传入SpanContext实例,支持基于字符格式的map的标识符,以及支持基于字符map的Carrier,三个参数
  3. Carrier将在Carrier对基于字符的map(参数2)填充必要的数据;客户端应用程序会在自制的HTTP协议中,对这个map进行编码(如:添加到HTTP头中)
  4. 进行HTTP调用,将数据进行跨进程传输
  5. 现在,在服务端进程进行处理。应用程序从自制的HTTP协议中解码出上文所述的map(参数2),并通过他,初始化基于字符map的Carrier
  6. 服务端进程调用,传入需要的操作名(operation name),支持就要字符格式的map的标识符,以及上面构建的Carrier
  7. 不考虑数据丢失,和其他错误,服务端现在有了和客户端追踪上下文中,一样的SpanContext

OpenTracing use cases, OpenTracing常见用例 文档中,可以找到其他使用案例。