分布式服务的Trace——Google Dapper & Twitter Zipkin
对于分布式在线服务,一个请求需要经过系统中多个模块,上百台机器的协作完成单次请求,典型场景就是Search Engine的一次用户检索,单靠人力无法掌握整个请求中各个阶段的性能开销,更无法快速的定位系统中性能瓶颈。Google Dapper文章描述了广泛用于Google内部服务的Trace Infrastruce—Dapper(原文地址见 这里, 译文地址见 这里 ),文章本身的很易懂,没有复杂、精巧的实现机制(好像也是g公司publish出来的文章的特点),有一些分布式在线服务经验的程序员都可以很好的理解(英文版),这里就只抽一些点出来记录。而Zipkin是Twitter开源出来的一个Trace系统组件,实现中就参考了Google Dapper,项目主页见http://twitter.github.io/zipkin/ 。
Google Dapper
目标: 无所不在的持续跟踪(ubiquitous deployment,and continuous monitoring),只有无所不在和持续,才能保证所有的问题都能被跟踪到,因为服务也是7*24的。为了做到这两点,Dapper对于这个Tracing组件,拆分出如下几个目标。
- 低消耗(Low overhead): 分布式在线系统对于性能、资源要求都很严格,Trace组件必须对服务影响足够小,否则一定不会被启用。
- 应用层透明(Application-Level Transparency): 应用层开发者不需要对Trace组件关心,Trace嵌入到基础通用库中,提供高稳定性,而如果Trace需要应用开发者配合,那可能会引入额外的bug导致Trace系统更加脆弱。
- 扩展性(Scalability): Google的服务在线集群数量可想而知,一次检索就可能跨越上百台甚至成千台机器,因此这个Trace Infrastructure的扩展性也很重要。
- 快速的数据分析(Analysis Quickly): 数据快速导出和分析,能够快速的定位系统的异常,及时应对,提升系统的稳定性。
典型检索场景: 单次user检索贯串前后端多个模块,模块之间使用rpc进行通信,如下图,request需要系统中由不同团队、不同语言构成的多个模块通信协作完成,典型的一个自上而下的调用序列。Dapper的采集方式基于这种调用序列构成的树,拆分为不同的span(一次rpc远程调用),通过全局唯一的Trace Id串起来,span指向自己的parent span。
Trace组件的实践经验:
- 低采样率: 在保证数据误差的情况下,Dapper尽可能采用低的采样率来保证Trace的low overhead,一方面会给应用层自己的Annotation标记留出更多的性能空间,也可以在硬盘上保存更长时间的数据。
- 带外数据收集(out-of-band): Trace组件将span信息落地到本机,使用守护进程进行带外收集,可以避免Trace数据占用检索带宽。同时,并不是所有的调用过程都是完美的嵌套(prefect nested),有些组件会在下游返回结果前先向上游汇报一些结果,不适合将Trace结果与检索数据绑定。
- 数据隐私: Trace系统仅面向的是系统的性能开销诊断,考虑到隐私,Dapper只记录RPC的名称和性能开销,对于一些策略和业务数据,可以考虑使用类似Online Debug的系统实现,引入权限控制。
- 精简的基础lib: Dapper基础代码库很精简,便于植入各个通用库,保证稳定性和性能开销,在大部分控制流的基础库上进行了默认植入,来记录各个span的开销,其中C++的代码实现不到1000行。
- 控制Dapper数据采集守护进程的资源消耗: 机器上的一些例行任务,例如数据统计脚本运行、外部数据的自动更新等,也会对部署在机器上的进程产生性能影响。因此,Dapper守护进程的性能数据采集,对使用的网络、CPU都非常谨慎,Dapper守护进程为内核scheduler最低的优先级。
- 开放API和易用的UI: 方便的DAPI来访问导入到Bigtable中的采集数据,其他使用方可以使用API来构建web应用、可执行的command等(根据经验,RESTFul是一个不错的选择),并且默认提供一个良好的UI,便于查询。
Twitter Zipkin
Zipkin的项目主页上说到——Zipkin is a distributed tracing system that helps us gather timing data for all the disparate services at Twitter. It manages both the collection and lookup of this data through a Collector and a Query service. We closely modelled Zipkin after the Google Dapper paper 。看Zipkin的Architecture文档,描述上也都实现了一些Dapper文章中核心的描述。
Zipkin Infrastructure
- Trace数据的收集系统,使用了Facebook开源的Scribe作为日志传输通路,将不同service上的Trace数据传输到Zipkin/Hadoop。
- Zipkin的Storage系统最先使用的是Cassandra,后来加入了Redis, HBase, MySQL, PostgreSQL, SQLite, and H2等存储系统的支持,猜测这个也是Twitter当时放弃Cassandra转回MySQL有关。
- Twitter中广泛使用了一个叫做Finagle的Java Async RPC服务,将trace library嵌入到这个Finagle中就成为支持系统跟踪的一个自然而然的选项。这个Library也是基于Dapper文章中的几个概念(Annotation—包含host、时间戳的key/value;Span—一次特定的rpc调用,包含多个Annotation;Trace—由一组span组成,共享一个root span)。
- Client和Server在通信时,会在tracing header信息中加上TraceId(标识整个trace)、SpanId(独立的RPC request)、可选的Parent SpanId(一个服务会调用多个下游RPC服务)以及Sample boolean变量(标识本次是否采集)。
- 通过Thrift Api来提供数据的访问查询功能,并搭建自己的UI(基于Rails,使用了d3js)。
按照主页上描述,Server接收到Client请求后,解析Trace头的处理逻辑流程图,自己画的简易流程图(字很丑- -)。
不论是Dapper的文章,还是Zipkin的主页,对于分布式系统中Trace Infrastructure的描述都比较简单,只提到了一些必须的基础组件,以及每个组件需要关注的问题。但结合实际工作中经验,这样完备的Trace系统对分布式在线服务的异常诊断、性能优化有非常大的帮助,而这样的Trace组件又对各个基础组件的要求非常高。健壮、完备的基础组件对于这样系统的搭建是非常有益的。