当前位置:   article > 正文

Dapper,大规模分布式系统的跟踪系统

dapper作者

编译自 Dapper, a Large-Scale Distributed Systems Tracing Infrastructure 作者:Benjamin H. Sigelman, Luiz Andre Barroso, Mike Burrows, Pat Stephenson, Manoj Plakal, Donald Beaver, Saul Jaspan, Chandan Shanbhag

背景

现在的互联网服务大多是通过复杂的大规模分布式集群实现的。Google生产环境中的各种应用是由不同的团队使用不同的编程语言在不同的软件模块集上开发实现的。这些应用可能分布在几千台服务器上,横跨多个不同的数据中心。

传统跟踪系统往往是整个应用系统的一部分,侵入性强,监控代码段的植入可能导致系统故障,监控间隔的设定也往往会影响整体性能。

因此,Google内部开发了Dapper,其设计初衷是追踪在线服务系统的请求处理过程。一次搜索请求往往会经过不同机器甚至不同集群上的多个子系统的处理。请求处理出现异常后,需要快速发现问题,并准确定位到出现问题的环节。

由于异常无法预料且难以重现,因此必须持续跟踪系统行为。此外,跟踪要覆盖全面,避免遗漏重要环节。

因此,Dapper需要做到低损耗、对应用透明性且可扩展;同时产生的跟踪数据需要可以被快速分析,帮助用户实时获取在线服务状态。

实现方法

低损耗:采样跟踪,并对收集的跟踪数据二次采样

对应用的透明性:修改线程、控制流、RPC等基础库代码,植入跟踪代码。Google的应用使用的是相同的线程、控制流、RPC等基础库代码,所以仅修改基础款代码就能够实现跟踪功能。

  • 在跟踪线程处理的过程中,Dapper会将一个trace context关联到线程本地存储中。trace context中包含了span的相关属性,比如trace和span id。
  • 对于异步线程处理过程,Google开发人员采用通用的控制流库实现回调,将异步线程调度到一个线程池或执行器中进行调用。Dapper保证所有回调都会保存创建者的trace context;同时当该回调被调用时,该trace context也会被关联到对应的线程。这样,Dapper就可以实现这种异步处理过程的跟踪。

植入代码的功能主要包括span创建、采样、本地磁盘日志写入。因为这部分代码会被很多应用所依赖,维护和bug修复难度高,因此需要非常稳定和健壮;同时还要足够轻量。这部分代码的C++实现总共不到1000行,Java实现不到800行。

Dapper支持用户直接获取Tracer对象,并输出自定义信息,用户可以输出自己任意想输出的内容。为防止过度输出,Dapper设置了可配置参数的上限。

跟踪过程中会产生一个全局唯一ID(在Dapper中是一个64位整数),用于标记该请求。Dapper的一个trace(跟踪过程)实际上是一颗树,树中的节点被称为一个span,根节点被称为root span,见下图。

一个span可能包含来自多个主机的信息。实际上,每个RPC span都包含了来自客户端和服务端的信息。对于客户端的时间与服务端的差异,可以根据“RPC是在服务端接收到请求前由客户端发起的,针对该RPC的响应则是由服务端在客户端接收到前发出的"这一事实确定服务端响应时间戳的上下限。

Dapper的整个数据收集过程如下图所示:

首先将span数据写入本地日志文件,然后将数据收集并写入Bigtable,每个trace记录在表中记录为一行。Bigtable的稀疏表结构非常适合存储trace记录,因为每条记录的span数量是任意的。

整个收集过程是out-of-band的,独立于请求处理过程,从而避免影响请求处理。

Dapper提供API,允许用户直接访问跟踪数据。Google开发人员可以基于API开发通用的或针对具体应用的分析工具,从而极大地提高了Dapper的功能和应用范围。

跟踪消耗

如果产生的消耗过高,跟踪系统便会被弃用,因此低消耗非常重要。采样可以减少消耗,但简单的采样可能导致采样结果不具有代表性。Dapper通过自适应采样机制满足性能和代表性要求。

对Dapper来说,数据收集和分析可以临时关掉,但数据生成必须一直进行,因此trace的生成消耗是最关键的。其中,span和annotation的创建和销毁消耗最大。由于需要产生一个全局唯一的trace id,根span的创建和销毁平均需要204ns;而普通span的创建和销毁只需要176ns。如果span没有被采样到,那么对其添加annotation的开销(约9ns)基本可以忽略;如果被采样到的话,则平均开销是40ns。上述测试是在2.2GHZ的x86服务器上进行的。

本地磁盘写入是Dapper运行库中消耗最高的操作,但可以异步化、批量化,因此基本上只会对吞吐量高的应用产生影响。

Dapper后台进程读出跟踪数据也会产生消耗。但Dapper守护进程的CPU开销始终在0.3%以下,内存占用也很少。

此外也会产生的网络消耗,但由于每个span平均只有426字节大小,网络消耗只占整个产品系统流量的0.01%不到。

对于每个请求都可能产生大量跟踪数据的应用来说,Dapper会通过采样减少消耗。最初,Dapper从每1024个请求中选择一个进行跟踪,这种模式适用于请求量高的服务,可以跟踪到有价值的信息;但对于负载不高的服务,这种方式可能会导致采样频率过低,遗漏重要信息。最后,Dapper选择以时间为采样单位,在单位时间内进行固定次数的采样,从而有效地控制采样频率和消耗。

应用

用户可以通过DAPI(Dapper“Depot API”)直接访问跟踪数据。DAPI提供如下几种访问方式:

  • 指定trace id进行访问;
  • 大规模批量访问。用户可以通过MapReduce job并行访问,只需要实现一个以Dapper trace为参数的虚函数,在该函数内完成自己的处理即可,框架负责为用户指定时间段内的所有trace调用该函数;
  • 通过索引访问。Dapper会为跟踪数据建立索引,用户可通过索引进行查询。由于trace id是随机生成的,因此用户通常需要通过服务名或机器名进行检索(实际上Dapper是按照(服务名,机器名,时间戳)进行索引的)。

大部分用户都是通过一个交互式web接口来使用Dapper的,典型流程如下图所示:

1.用户输入感兴趣的服务和时间窗口,选择相应的跟踪模式(图中为span名称)以及关心的度量参数(图中为服务延迟)

2.页面会展示指定服务的所有分布式执行过程的性能摘要,用户可以根据需要对这些执行过程排序,然后选择其中一个详细查看

3.选定某个执行过程后,页面会展示关于该执行过程的图形化描述,用户可点击选择自己关心的过程

4.系统根据用户在1中选择的度量参数以及在3中选择的具体过程,在页面上展示直方图。上图中的直方图展示了getdocs的延迟分布,用户可点击右侧的example,选择具体的执行过程进行查看

5.显示关于该执行过程的具体信息,上方是时间轴,用户可展开或折叠下方执行过程,查看该执行过程各个组成部分的开销,其中绿色代表处理时间,蓝色代表网络耗时。

总结

在开发过程中,Dapper可以用于改善性能(分析请求延迟、发现关键路径上不必要的串行化),进行正确性检查(用户请求是否正确发送给了服务提供者),理解系统(请求处理可能依赖很多其他系统,Dapper帮助用户了解总体延迟,重新设计最小化依赖),测试(新代码release前要通过Dapper跟踪测试,验证系统行为和性能)。

与异常监控系统进行集成。如果异常发生在采样到的Dapper tracer上下文中,那么相应的trace和span id还会被作为异常报告的元数据;前端的异常监控服务也会提供相应链接指向跟踪系统,从而帮助用户了解异常发生时的情况。

解决长尾延迟,帮助用户分析复杂系统环境下的延迟问题。网络性能的瞬时下降不会影响系统的吞吐率,但对延迟有很大影响。很多开销昂贵的查询模式是由未预料到的服务间的交互造成的,Dapper能够更好地发现这种问题。

帮助用户进行服务间依赖的推理。Google维护了非常多的集群,每个集群承载了各种各样的任务,而任务间可能存在依赖关系。但各个任务需要精确知道其所依赖的服务信息,才能帮助发现瓶颈或进行服务的移动。服务间的依赖关系复杂且处于动态变化中,单纯依赖配置文件很难进行判断。而Dapper的trace信息和DAPIMapReduce接口则可以自动确定服务间依赖关系。

帮助网络管理员对跨集群的网络活动进行应用层面的分析,帮助发现某些昂贵的网络请求开销的产生原因。

很多存储系统都是共享的。例如,GFS有很多用户,有些用户是直接访问GFS,有些则可能通过Bigtable对GFS进行访问。如果没有Dapper,这种共享式系统将很难调试。通过Dapper提供的数据,共享服务的owner可以方便地根据各项指标(比如网络负载,请求耗时)对用户进行排序。

Dapper将跟踪数据开放给用户后,激发了用户创造力,发现了很多意料之外的用途。没有跟踪功能的应用只需要用新的库重新编译程序,便可获得跟踪功能,迁移非常方便。

待改进之处

合并产生的影响。我们通常假设各种子系统会一次处理一个请求。但是某些情况下,请求会被缓存,然后一次性地在一组请求上执行某个操作(比如磁盘写入)。在这种情况下,被追踪的请求拿到的并不是其本身的处理过程。

对批量处理进行跟踪。虽然Dapper是为在线服务系统而设计,初衷是理解用户发送请求给Google后产生的一系列系统行为,但离线的数据处理也有这样的需求。因此,可以将trace id与某些有意义的工作单元进行关联,比如输入数据里的一个key(或者是key range)。

寻找根本原因。Dapper可以迅速找到系统短板,但往往无法高效地寻找根本原因。例如,某个请求变慢的原因可能并不在其自身,而是因为在它之前已经有很多请求在排队。用户可以在应用层将某些参数(比如队列大小)交给跟踪系统。

记录内核级信息。很多工具可以进行内核执行过程的跟踪和profiling,但直接将内核级信息捆绑到一个应用层的trace context中很难优雅地实现。折中的解决方案是在应用层获取内核活动参数的快照,然后将快照与活动span关联起来。

Dapper的实现 受Dapper启发,很多厂商纷纷开发了自己的跟踪系统,例如宜信的UAVStack、阿里的鹰眼、Twitter的Zipkin、大众点评的cat以及京东的hydra。

参考资料

  1. Dapper, a Large-Scale Distributed Systems Tracing Infrastructure
  2. https://bigbully.github.io/Dapper-translation/
  3. https://www.cnblogs.com/rongfengliang/p/6209301.html

微信扫一扫,关注该公众号

转载于:https://juejin.im/post/5c886d34f265da2db912a9f2

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/380714
推荐阅读
  

闽ICP备14008679号