使用TChannel进行追踪
2015年初,我们开始开发TChannel,这是一种适用于RPC的网络多路复用和框架协议。该协议的设计目标之一是将类似于Dapper的分布式追踪能力融入协议中,并为其提供最优秀的支持。为了实现这一目标,TChannel 协议规范将追踪字段直接定义到了二进制格式中。
spanid:8 parentid:8 traceid:8 traceflags:1
追踪字段作为二进制格式的一部分已包含在TChannel协议规范中。
除了协议规范,我们还发布了多个开源客户端库,用于以不同语言实现该协议。这些库的设计原则之一是让应用程序需要用到的请求上下文这一概念能够从服务器端点贯穿至下游的调用站点。例如在tchannel-go中,让出站调用使用JSON进行编码的签名需要通过第一个参数提供上下文:
func (c *Client) Call(ctx Context, method string, arg, resp interface{}) error {..}
Tchannel库使得应用程序开发者在编写自己的代码时始终将分布式上下文传播这一概念铭记于心。
通过将所传输内容以及内存中的上下文对象之间的追踪上下文进行安排,并围绕服务处理程序和出站调用创建追踪Span,客户端库内建了对分布式追踪的支持。从内部来看,这些Span在格式上与Zipkin追踪系统几乎完全相同,也使用了Zipkin所定义的注释,例如“cs”(Client Send)和“cr”(Client Receive)。Tchannel使用追踪报告程序(Reporter)接口将收集到的进程外追踪Span发送至追踪系统的后端。该技术自带的库默认包含一个使用Tchannel本身和Hyperbahn实现的报告程序以及发现和路由层,借此将Thrift格式的Span发送至收集器群集。
Tchannel客户端库已经比较近似于我们所需要的分布式追踪系统,该客户端库提供了下列构建块:
• 追踪上下文的进程间传播以及带内请求
• 通过编排API记录追踪Span
• 追踪上下文的进程内传播
• 将进程外追踪数据报告至追踪后端所需的格式和机制
该系统唯独缺少了追踪后端本身。追踪上下文的传输格式和报表程序使用的默认Thrift格式在设计上都可以非常简单直接地将Tchannel与Zipkin后端集成,然而当时只能通过Scribe将Span发送至Zipkin,而Zipkin只支持使用Cassandra格式的数据存储。此外当时我们对这些技术没什么经验,因此我们开发了一套后端原型系统,并结合Zipkin UI的一些自定义组件构建了一个完整的追踪系统。
后端原型系统架构:Tchannel生成的追踪记录推送给自定义收集器、自定义存储,以及开源的Zipkin UI。
分布式追踪系统在谷歌和Twitter等主要技术公司获得的成功意味着这些公司中广泛使用的RPC框架、Stubby和Finagle是行之有效的。
同理,Tchannel自带的追踪能力也是一个重大的飞跃。我们部署的后端原型系统已经开始从数十种服务中收集追踪信息。随后我们使用Tchannel构建了更多服务,但在生产环境中全面推广和广泛使用依然有些困难。该后端原型以及所使用的Riak/Solr存储系统无法妥善缩放以适应Uber的流量,同时很多查询功能依然无法与Zipkin UI实现足够好的互操作。尽管新构建的服务大量使用了Tchannel, Uber依然有大量服务尚未在RPC过程中使用Tchannel,实际上承担核心业务的大部分服务都没有使用Tchannel。这些服务主要是通过四大编程语言(Node.js、Python、Go和Java)实现的,在进程间通信方面使用了多种不同的框架。这种异构的技术环境使得Uber在分布式追踪系统的构建方面会面临比谷歌和Twitter更严峻的挑战。