概述 前端
当代的互联网的服务,一般都是用复杂的、大规模分布式集群来实现的。互联网应用构建在不一样的软件模块集上,这些软件模块,有多是由不一样的团队开发、可能使用不一样的编程语言来实现、有可能布在了几千台服务器,横跨多个不一样的数据中心。所以,就须要一些可以帮助理解系统行为、用于分析性能问题的工具。 程序员
Dapper--Google生产环境下的分布式跟踪系统,应运而生。那么咱们就来介绍一个大规模集群的跟踪系统,它是如何知足一个低损耗、应用透明的、大范围部署这三个需求的。固然Dapper设计之初,参考了一些其余分布式系统的理念,尤为是Magpie和X-Trace,可是咱们之因此能成功应用在生产环境上,还须要一些画龙点睛之笔,例如采样率的使用以及把代码植入限制在一小部分公共库的改造上。 web
自从Dapper发展成为一流的监控系统以后,给其余应用的开发者和运维团队帮了大忙,因此咱们今天才发表这篇论文,来汇报一下这两年来,Dapper是怎么构建和部署的。Dapper最初只是做为一个自给自足的监控工具起步的,但最终进化成一个监控平台,这个监控平台促生出多种多样的监控工具,有些甚至已经不是由Dapper团队开发的了。下面咱们会介绍一些使用Dapper搭建的分析工具,分享一下这些工具在google内部使用的统计数据,展示一些使用场景,最后会讨论一下咱们迄今为止从Dapper收获了些什么。 算法
咱们开发Dapper是为了收集更多的复杂分布式系统的行为信息,而后呈现给Google的开发者们。这样的分布式系统有一个特殊的好处,由于那些大规模的低端服务器,做为互联网服务的载体,是一个特殊的经济划算的平台。想要在这个上下文中理解分布式系统的行为,就须要监控那些横跨了不一样的应用、不一样的服务器之间的关联动做。 数据库
下面举一个跟搜索相关的例子,这个例子阐述了Dapper能够应对哪些挑战。好比一个前段服务可能对上百台查询服务器发起了一个Web查询,每个查询都有本身的Index。这个查询可能会被发送到多个的子系统,这些子系统分别用来处理广告、进行拼写检查或是查找一些像图片、视频或新闻这样的特殊结果。根据每一个子系统的查询结果进行筛选,获得最终结果,最后汇总到页面上。咱们把这种搜索模型称为“全局搜索”(universal search)。总的来讲,这一次全局搜索有可能调用上千台服务器,涉及各类服务。并且,用户对搜索的耗时是很敏感的,而任何一个子系统的低效都致使致使最终的搜索耗时。若是一个工程师只能知道这个查询耗时不正常,可是他无从知晓这个问题究竟是由哪一个服务调用形成的,或者为何这个调用性能差强人意。首先,这个工程师可能没法准确的定位到此次全局搜索是调用了哪些服务,由于新的服务、乃至服务上的某个片断,都有可能在任什么时候间上过线或修改过,有多是面向用户功能,也有多是一些例如针对性能或安全认证方面的功能改进。其次,你不能苛求这个工程师对全部参与此次全局搜索的服务都了如指掌,每个服务都有多是由不一样的团队开发或维护的。再次,这些暴露出来的服务或服务器有可能同时还被其余客户端使用着,因此此次全局搜索的性能问题甚至有多是由其余应用形成的。举个例子,一个后台服务可能要应付各类各样的请求类型,而一个使用效率很高的存储系统,好比Bigtable,有可能正被反复读写着,由于上面跑着各类各样的应用。 编程
上面这个案例中咱们能够看到,对Dapper咱们只有两点要求:无所不在的部署,持续的监控。无所不在的重要性不言而喻,由于在使用跟踪系统的进行监控时,即使只有一小部分没被监控到,那么人们对这个系统是否是值得信任都会产生巨大的质疑。另外,监控应该是7x24小时的,毕竟,系统异常或是那些重要的系统行为有可能出现过一次,就很难甚至不太可能重现。那么,根据这两个明确的需求,咱们能够直接推出三个具体的设计目标: 后端
1.低消耗:跟踪系统对在线服务的影响应该作到足够小。在一些高度优化过的服务,即便一点点损耗也会很容易察觉到,并且有可能迫使在线服务的部署团队不得不将跟踪系统关停。 api
2.应用级的透明:对于应用的程序员来讲,是不须要知道有跟踪系统这回事的。若是一个跟踪系统想生效,就必须须要依赖应用的开发者主动配合,那么这个跟踪系统也太脆弱了,每每因为跟踪系统在应用中植入代码的bug或疏忽致使应用出问题,这样才是没法知足对跟踪系统“无所不在的部署”这个需求。面对当下想Google这样的快节奏的开发环境来讲,尤为重要。 安全
3.延展性:Google至少在将来几年的服务和集群的规模,监控系统都应该能彻底把控住。 服务器
一个额外的设计目标是为跟踪数据产生以后,进行分析的速度要快,理想状况是数据存入跟踪仓库后一分钟内就能统计出来。尽管跟踪系统对一小时前的旧数据进行统计也是至关有价值的,但若是跟踪系统能提供足够快的信息反馈,就能够对生产环境下的异常情况作出快速反应。
作到真正的应用级别的透明,这应该是当下面临的最挑战性的设计目标,咱们把核心跟踪代码作的很轻巧,而后把它植入到那些无所不在的公共组件种,好比线程调用、控制流以及RPC库。使用自适应的采样率能够使跟踪系统变得可伸缩,并下降性能损耗,这些内容将在第4.4节中说起。结果展现的相关系统也须要包含一些用来收集跟踪数据的代码,用来图形化的工具,以及用来分析大规模跟踪数据的库和API。虽然单独使用Dapper有时就足够让开发人员查明异常的来源,可是Dapper的初衷不是要取代全部其余监控的工具。咱们发现,Dapper的数据每每侧重性能方面的调查,因此其余监控工具也有他们各自的用处。
分布式系统跟踪工具的设计空间已经被一些优秀文章探索过了,其中的Pinpoint[9]、Magpie[3]和X-Trace[12]和Dapper最为相近。这些系统在其发展过程的早期倾向于写入研究报告中,即使他们还没来得及清楚地评估系统当中一些设计的重要性。相比之下,因为Dapper已经在大规模生产环境中摸爬滚打了多年,通过这么多生产环境的验证以后,咱们认为这篇论文最适合重点阐述在部署Dapper的过程当中咱们有那些收获,咱们的设计思想是如何决定的,以及以什么样的方式实现它才会最有用。Dappe做为一个平台,承载基于Dapper开发的性能分析工具,以及Dapper自身的监测工具,它的价值在于咱们能够在回顾评估中找出一些意想不到的结果。
虽然Dapper在许多高阶的设计思想上吸收了Pinpoint和Magpie的研究成果,但在分布式跟踪这个领域中,Dapper的实现包含了许多新的贡献。例如,咱们想实现低损耗的话,特别是在高度优化的并且趋于极端延迟敏感的Web服务中,采样率是很必要的。或许更使人惊讶的是,咱们发现即使是1/1000的采样率,对于跟踪数据的通用使用层面上,也能够提供足够多的信息。
咱们的系统的另外一个重要的特征,就是咱们能实现的应用级的透明。咱们的组件对应用的侵入被先限制在足够低的水平上,即便想Google网页搜索这么大规模的分布式系统,也能够直接进行跟踪而无需加入额外的标注(Annotation)。虽然因为咱们的部署系统有幸是必定程度的同质化的,因此更容易作到对应用层的透明这点,可是咱们证实了这是实现这种程度的透明性的充分条件。
图1:这个路径由用户的X请求发起,穿过一个简单的服务系统。用字母标识的节点表明分布式系统中的不一样处理过程。
分布式服务的跟踪系统须要记录在一次特定的请求后系统中完成的全部工做的信息。举个例子,图1展示的是一个和5台服务器相关的一个服务,包括:前端(A),两个中间层(B和C),以及两个后端(D和E)。当一个用户(这个用例的发起人)发起一个请求时,首先到达前端,而后发送两个RPC到服务器B和C。B会立刻作出反应,可是C须要和后端的D和E交互以后再返还给A,由A来响应最初的请求。对于这样一个请求,简单实用的分布式跟踪的实现,就是为服务器上每一次你发送和接收动做来收集跟踪标识符(message identifiers)和时间戳(timestamped events)。
为了将全部记录条目与一个给定的发起者(例如,图1中的RequestX)关联上并记录全部信息,如今有两种解决方案,黑盒(black-box)和基于标注(annotation-based)的监控方案。黑盒方案[1,15,2]假定须要跟踪的除了上述信息以外没有额外的信息,这样使用统计回归技术来推断二者之间的关系。基于标注的方案[3,12,9,16]依赖于应用程序或中间件明确地标记一个全局ID,从而链接每一条记录和发起者的请求。虽然黑盒方案比标注方案更轻便,他们须要更多的数据,以得到足够的精度,由于他们依赖于统计推论。基于标注的方案最主要的缺点是,很明显,须要代码植入。在咱们的生产环境中,由于全部的应用程序都使用相同的线程模型,控制流和RPC系统,咱们发现,能够把代码植入限制在一个很小的通用组件库中,从而实现了监测系统的应用对开发人员是有效地透明。
咱们倾向于认为,Dapper的跟踪架构像是内嵌在RPC调用的树形结构。然而,咱们的核心数据模型不仅局限于咱们的特定的RPC框架,咱们还能跟踪其余行为,例如Gmail的SMTP会话,外界的HTTP请求,和外部对SQL服务器的查询等。从形式上看,咱们的Dapper跟踪模型使用的树形结构,Span以及Annotation。
在Dapper跟踪树结构中,树节点是整个架构的基本单元,而每个节点又是对span的引用。节点之间的连线表示的span和它的父span直接的关系。虽然span在日志文件中只是简单的表明span的开始和结束时间,他们在整个树形结构中倒是相对独立的,任何RPC相关的时间数据、零个或多个特定应用程序的Annotation的相关内容会在2.3节中讨论。
图2:5个span在Dapper跟踪树种短暂的关联关系
在图2中说明了span在一个大的跟踪过程当中是什么样的。Dapper记录了span名称,以及每一个span的ID和父ID,以重建在一次追踪过程当中不一样span之间的关系。若是一个span没有父ID被称为root span。全部span都挂在一个特定的跟踪上,也共用一个跟踪id(在图中未示出)。全部这些ID用全局惟一的64位整数标示。在一个典型的Dapper跟踪中,咱们但愿为每个RPC对应到一个单一的span上,并且每个额外的组件层都对应一个跟踪树型结构的层级。
图3:在图2中所示的一个单独的span的细节图
图3给出了一个更详细的典型的Dapper跟踪span的记录点的视图。在图2中这种某个span表述了两个“Helper.Call”的RPC(分别为server端和client端)。span的开始时间和结束时间,以及任何RPC的时间信息都经过Dapper在RPC组件库的植入记录下来。若是应用程序开发者选择在跟踪中增长他们本身的注释(如图中“foo”的注释)(业务数据),这些信息也会和其余span信息同样记录下来。
记住,任何一个span能够包含来自不一样的主机信息,这些也要记录下来。事实上,每个RPC span能够包含客户端和服务器两个过程的注释,使得连接两个主机的span会成为模型中所说的span。因为客户端和服务器上的时间戳来自不一样的主机,咱们必须考虑到时间误差。在咱们的分析工具,咱们利用了这个事实:RPC客户端发送一个请求以后,服务器端才能接收到,对于响应也是同样的(服务器先响应,而后客户端才能接收到这个响应)。这样一来,服务器端的RPC就有一个时间戳的一个上限和下限。
Dapper能够以对应用开发者近乎零浸入的成本对分布式控制路径进行跟踪,几乎彻底依赖于基于少许通用组件库的改造。以下:
Dapper的跟踪数据是独立于语言的,不少在生产环境中的跟踪结合了用C++和Java写的进程的数据。在3.2节中,咱们讨论应用程序的透明度时咱们会把这些理论的是如何实践的进行讨论。
上述植入点足够推导出复杂的分布式系统的跟踪细节,使得Dapper的核心功能在不改动Google应用的状况下可用。然而,Dapper还容许应用程序开发人员在Dapper跟踪的过程当中添加额外的信息,以监控更高级别的系统行为,或帮助调试问题。咱们容许用户经过一个简单的API定义带时间戳的Annotation,核心的示例代码入图4所示。这些Annotation能够添加任意内容。为了保护Dapper的用户意外的过度热衷于日志的记录,每个跟踪span有一个可配置的总Annotation量的上限。可是,应用程序级的Annotation是不能替代用于表示span结构的信息和记录着RPC相关的信息。
除了简单的文本Annotation,Dapper也支持的key-value映射的 Annotation,提供给开发人员更强的跟踪能力,如持续的计数器,二进制消息记录和在一个进程上跑着的任意的用户数据。键值对的Annotation方式用来在分布式追踪的上下文中定义某个特定应用程序的相关类型。
低损耗的是Dapper的一个关键的设计目标,由于若是这个工具价值未被证明但又对性能有影响的话,你能够理解服务运营人员为何不肯意部署它。何况,咱们想让开发人员使用Annotation的API,而不用担忧额外的开销。咱们还发现,某些类型的Web服务对植入带来的性能损耗确实很是敏感。所以,除了把Dapper的收集工做对基本组件的性能损耗限制的尽量小以外,咱们还有进一步控制损耗的办法,那就是遇到大量请求时只记录其中的一小部分。咱们将在4.4节中讨论跟踪的采样率方案的更多细节。
图5:Dapper收集管道的总览
Dapper的跟踪记录和收集管道的过程分为三个阶段(参见图5)。首先,span数据写入(1)本地日志文件中。而后Dapper的守护进程和收集组件把这些数据从生产环境的主机中拉出来(2),最终写到(3)Dapper的Bigtable仓库中。一次跟踪被设计成Bigtable中的一行,每一列至关于一个span。Bigtable的支持稀疏表格布局正适合这种状况,由于每一次跟踪能够有任意多个span。跟踪数据收集(即从应用中的二进制数据传输到中央仓库所花费的时间)的延迟中位数少于15秒。第98百分位的延迟(The 98th percentile latency)每每随着时间的推移呈现双峰型;大约75%的时间,第98百分位的延迟时间小于2分钟,可是另外大约25%的时间,它能够增涨到几个小时。
Dapper还提供了一个API来简化访问咱们仓库中的跟踪数据。 Google的开发人员用这个API,以构建通用和特定应用程序的分析工具。第5.1节包含更多如何使用它的信息。
tip1:带外数据:传输层协议使用带外数据(out-of-band,OOB)来发送一些重要的数据,若是通讯一方有重要的数据须要通知对方时,协议可以将这些数据快速地发送到对方。为了发送这些数据,协议通常不使用与普通数据相同的通道,而是使用另外的通道。
tip2:这里指的in-band策略是把跟踪数据随着调用链进行传送,out-of-band是经过其余的链路进行跟踪数据的收集,Dapper的写日志而后进行日志采集的方式就属于out-of-band策略
Dapper系统请求树树自身进行跟踪记录和收集带外数据。这样作是为两个不相关的缘由。首先,带内收集方案--这里跟踪数据会以RPC响应头的形式被返回--会影响应用程序网络动态。在Google里的许多规模较大的系统中,一次跟踪成千上万的span并很多见。然而,RPC回应大小--甚至是接近大型分布式的跟踪的根节点的这种状况下-- 仍然是比较小的:一般小于10K。在这种状况下,带内Dapper的跟踪数据会让应用程序数据和倾向于使用后续分析结果的数据量相形见绌。其次,带内收集方案假定全部的RPC是完美嵌套的。咱们发现,在全部的后端的系统返回的最终结果以前,有许多中间件会把结果返回给他们的调用者。带内收集系统是没法解释这种非嵌套的分布式执行模式的。
记录必定量的RPC有效负载信息将丰富Dapper的跟踪能力,由于分析工具可以在有效载荷数据(方法传递的参数)中找到相关的样例,这些样例能够解释被监控系统的为什么表现异常。然而,有些状况下,有效载荷数据可能包含的一些不该该透露给未经受权用户(包括正在debug的工程师)的内部信息。
因为安全和隐私问题是不可忽略的,dapper中的虽然存储RPC方法的名称,但在这个时候不记录任何有效载荷数据。相反,应用程序级别的Annotation提供了一个方便的可选机制:应用程序开发人员能够在span中选择关联那些为之后分析提供价值的数据。
Dapper还提供了一些安全上的便利,是它的设计者事先没有预料到的。经过跟踪公开的安全协议参数,Dapper能够经过相应级别的认证或加密,来监视应用程序是否知足安全策略。例如。Dapper还能够提供信息,以基于策略的的隔离系统按预期执行,例如支撑敏感数据的应用程序不与未经受权的系统组件进行了交互。这样的测算提供了比源码审核更强大的保障。
Dapper做为咱们生产环境下的跟踪系统已经超过两年。在本节中,咱们会汇报系统状态,把重点放在Dapper如何知足了咱们的目标——无处不在的部署和应用级的透明。
也许Dapper代码中中最关键的部分,就是对基础RPC、线程控制和流程控制的组件库的植入,其中包括span的建立,采样率的设置,以及把日志写入本地磁盘。除了作到轻量级,植入的代码更须要稳定和健壮,由于它与海量的应用对接,维护和bug修复变得困难。植入的核心代码是由未超过1000行的C++和不超过800行Java代码组成。为了支持键值对的Annotation还添加了额外的500行代码。
Dapper的渗透能够总结为两个方面:一方面是能够建立Dapper跟踪的过程(与Dapper植入的组件库相关),和生产环境下的服务器上在运行Dapper跟踪收集守护进程。Dapper的守护进程的分布至关于咱们服务器的简单的拓扑图,它存在于Google几乎全部的服务器上。这很难肯定精确的Dapper-ready进程部分,由于过程即使不产生跟踪信息Dapper也是无从知晓的。尽管如此,考虑到无处不在Dapper组件的植入库,咱们估计几乎每个Google的生产进程都是支持跟踪的。
在某些状况下Dapper的是不能正确的跟踪控制路径的。这些一般源于使用非标准的控制流,或是Dapper的错误的把路径关联归到不相关的事件上。Dapper提供了一个简单的库来帮助开发者手动控制跟踪传播做为一种变通方法。目前有40个C++应用程序和33个Java应用程序须要一些手动控制的追踪传播,不过这只是上千个的跟踪中的一小部分。也有很是小的一部分程序使用的非组件性质的通讯库(好比原生的TCP Socket或SOAP RPC),所以不能直接支持Dapper的跟踪。可是这些应用能够单独接入到Dapper中,若是须要的话。
考虑到生产环境的安全,Dapper的跟踪也能够关闭。事实上,它在部署的早起就是默认关闭的,直到咱们对Dapper的稳定性和低损耗有了足够的信心以后才把它开启。Dapper的团队偶尔会执行审查寻找跟踪配置的变化,来看看那些服务关闭了Dapper的跟踪。但这种状况很少见,并且一般是源于对监控对性能消耗的担心。通过了对实际性能消耗的进一步调查和测量,全部这些关闭Dapper跟踪都已经恢复开启了,不过这些已经不重要了。
程序员倾向于使用特定应用程序的Annotation,不管是做为一种分布式调试日志文件,仍是经过一些应用程序特定的功能对跟踪进行分类。例如,全部的Bigtable的请求会把被访问的表名也记录到Annotation中。目前,70%的Dapper span和90%的全部Dapper跟踪都至少有一个特殊应用的Annotation。
41个Java应用和68个C++应用中都添加自定义的Annotation为了更好地理解应用程序中的span在他们的服务中的行为。值得注意的是,迄今为止咱们的Java开发者比C++开发者更多的在每个跟踪span上采用Annotation的API。这多是由于咱们的Java应用的做用域每每是更接近最终用户(C++偏底层);这些类型的应用程序常常处理更普遍的请求组合,所以具备比较复杂的控制路径。
跟踪系统的成本由两部分组成:1.正在被监控的系统在生成追踪和收集追踪数据的消耗致使系统性能降低,2。须要使用一部分资源来存储和分析跟踪数据。虽然你能够说一个有价值的组件植入跟踪带来一部分性能损耗是值得的,咱们相信若是基本损耗能达到能够忽略的程度,那么对跟踪系统最初的推广会有极大的帮助。
在本节中,咱们会展示一下三个方面:Dapper组件操做的消耗,跟踪收集的消耗,以及Dapper对生产环境负载的影响。咱们还介绍了Dapper可调节的采样率机制如何帮咱们处理低损耗和跟踪表明性之间的平衡和取舍。
生成跟踪的开销是Dapper性能影响中最关键的部分,由于收集和分析能够更容易在紧急状况下被关闭。Dapper运行库中最重要的跟踪生成消耗在于建立和销毁span和annotation,并记录到本地磁盘供后续的收集。根span的建立和销毁须要损耗平均204纳秒的时间,而一样的操做在其余span上须要消耗176纳秒。时间上的差异主要在于须要在跟span上给此次跟踪分配一个全局惟一的ID。
若是一个span没有被采样的话,那么这个额外的span下建立annotation的成本几乎能够忽略不计,他由在Dapper运行期对ThreadLocal查找操做构成,这平均只消耗9纳秒。若是这个span被计入采样的话,会用一个用字符串进行标注--在图4中有展示--平均须要消耗40纳秒。这些数据都是在2.2GHz的x86服务器上采集的。
在Dapper运行期写入到本地磁盘是最昂贵的操做,可是他们的可见损耗大大减小,由于写入日志文件和操做相对于被跟踪的应用系统来讲都是异步的。不过,日志写入的操做若是在大流量的状况,尤为是每个请求都被跟踪的状况下就会变得能够察觉到。咱们记录了在4.3节展现了一次Web搜索的负载下的性能消耗。
读出跟踪数据也会对正在被监控的负载产生干扰。表1展现的是最坏状况下,Dapper收集日志的守护进程在高于实际状况的负载基准下进行测试时的cpu使用率。在生产环境下,跟踪数据处理中,这个守护进程历来没有超过0.3%的单核cpu使用率,并且只有不多量的内存使用(以及堆碎片的噪音)。咱们还限制了Dapper守护进程为内核scheduler最低的优先级,以防在一台高负载的服务器上发生cpu竞争。
Dapper也是一个带宽资源的轻量级的消费者,每个span在咱们的仓库中传输只占用了平均426的byte。做为网络行为中的极小部分,Dapper的数据收集在Google的生产环境中的只占用了0.01%的网络资源。
表1:Dapper守护进程在负载测试时的CPU资源使用率
每一个请求都会利用到大量的服务器的高吞吐量的线上服务,这是对有效跟踪最主要的需求之一;这种状况须要生成大量的跟踪数据,而且他们对性能的影响是最敏感的。在表2中咱们用集群下的网络搜索服务做为例子,咱们经过调整采样率,来衡量Dapper在延迟和吞吐量方面对性能的影响。
表2:网络搜索集群中,对不一样采样率对网络延迟和吞吐的影响。延迟和吞吐的实验偏差分别是2.5%和0.15%。
咱们看到,虽然对吞吐量的影响不是很明显,但为了不明显的延迟,跟踪的采样仍是必要的。然而,延迟和吞吐量的带来的损失在把采样率调整到小于1/16以后就所有在实验偏差范围内。在实践中,咱们发现即使采样率调整到1/1024仍然是有足够量的跟踪数据的用来跟踪大量的服务。保持Dapper的性能损耗基线在一个很是低的水平是很重要的,由于它为那些应用提供了一个宽松的环境使用完整的Annotation API而无惧性能损失。使用较低的采样率还有额外的好处,可让持久化到硬盘中的跟踪数据在垃圾回收机制处理以前保留更长的时间,这样为Dapper的收集组件给了更多的灵活性。
任何给定进程的Dapper的消耗和每一个进程单位时间的跟踪的采样率成正比。Dapper的第一个生产版本在Google内部的全部进程上使用统一的采样率,为1/1024。这个简单的方案是对咱们的高吞吐量的线上服务来讲是很是有用,由于那些感兴趣的事件(在大吞吐量的状况下)仍然颇有可能常常出现,而且一般足以被捕捉到。
然而,在较低的采样率和较低的传输负载下可能会致使错太重要事件,而想用较高的采样率就须要能接受的性能损耗。对于这样的系统的解决方案就是覆盖默认的采样率,这须要手动干预的,这种状况是咱们试图避免在dapper中出现的。
咱们在部署可变采样的过程当中,参数化配置采样率时,不是使用一个统一的采样方案,而是使用一个采样指望率来标识单位时间内采样的追踪。这样一来,低流量低负载自动提升采样率,而在高流量高负载的状况下会下降采样率,使损耗一直保持在控制之下。实际使用的采样率会随着跟踪自己记录下来,这有利于从Dapper的跟踪数据中准确的分析。
新的Dapper用户每每以为低采样率--在高吞吐量的服务下常常低至0.01%--将会不利于他们的分析。咱们在Google的经验使咱们相信,对于高吞吐量服务,积极采样(aggressive sampling)并不妨碍最重要的分析。若是一个显着的操做在系统中出现一次,他就会出现上千次。低吞吐量的服务--也许是每秒请求几十次,而不是几十万--能够负担得起跟踪每个请求,这是促使咱们下决心使用自适应采样率的缘由。
上述采样机制被设计为尽可能减小与Dapper运行库协做的应用程序中明显的性能损耗。Dapper的团队还须要控制写入中央资料库的数据的总规模,所以为达到这个目的,咱们结合了二级采样。
目前咱们的生产集群天天产生超过1TB的采样跟踪数据。Dapper的用户但愿生产环境下的进程的跟踪数据从被记录以后能保存至少两周的时间。逐渐增加的追踪数据的密度必须和Dapper中央仓库所消耗的服务器及硬盘存储进行权衡。对请求的高采样率还使得Dapper收集器接近写入吞吐量的上限。
为了维持物质资源的需求和渐增的Bigtable的吞吐之间的灵活性,咱们在收集系统自身上增长了额外的采样率的支持。咱们充分利用全部span都来自一个特定的跟踪并分享同一个跟踪ID这个事实,虽然这些span有可能横跨了数千个主机。对于在收集系统中的每个span,咱们用hash算法把跟踪ID转成一个标量Z,这里0<=Z<=1。若是Z比咱们收集系统中的系数低的话,咱们就保留这个span信息,并写入到Bigtable中。反之,咱们就抛弃他。经过在采样决策中的跟踪ID,咱们要么保存、要么抛弃整个跟踪,而不是单独处理跟踪内的span。咱们发现,有了这个额外的配置参数使管理咱们的收集管道变得简单多了,由于咱们能够很容易地在配置文件中调整咱们的全局写入率这个参数。
若是整个跟踪过程和收集系统只使用一个采样率参数确实会简单一些,可是这就不能应对快速调整在全部部署的节点上的运行期采样率配置的这个要求。咱们选择了运行期采样率,这样就能够优雅的去掉咱们没法写入到仓库中的多余数据,咱们还能够经过调节收集系统中的二级采样率系数来调整这个运行期采样率。Dapper的管道维护变得更容易,由于咱们就能够经过修改咱们的二级采样率的配置,直接增长或减小咱们的全局覆盖率和写入速度。
几年前,当Dapper还只是个原型的时候,它只能在Dapper开发者耐心的支持下使用。从那时起,咱们逐渐迭代的创建了收集组件,编程接口,和基于Web的交互式用户界面,帮助Dapper的用户独立解决本身的问题。在本节中,咱们会总结一下哪些的方法有用,哪些用处不大,咱们还提供关于这些通用的分析工具的基本的使用信息。
Dapper的“Depot API”或称做DAPI,提供在Dapper的区域仓库中对分布式跟踪数据一个直接访问。DAPI和Dapper跟踪仓库被设计成串联的,并且DAPI意味着对Dapper仓库中的元数据暴露一个干净和直观的的接口。咱们使用了如下推荐的三种方式去暴露这样的接口:
全部这三种访问模式把用户指向不一样的Dapper跟踪记录。正如第2.1节所述的,Dapper的由span组成的跟踪数据是用树形结构建模的,所以,跟踪数据的数据结构,也是一个简单的由span组成遍历树。Span就至关于RPC调用,在这种状况下,RPC的时间信息是可用的。带时间戳的特殊的应用标注也是能够经过这个span结构来访问的。
选择一个合适的自定义索引是DAPI设计中最具挑战性的部分。压缩存储要求在跟踪数据种创建一个索引的状况只比实际数据小26%,因此消耗是巨大的。最初,咱们部署了两个索引:第一个是主机索引,另外一个是服务名的索引。然而,咱们并无找到主机索引和存储成本之间的利害关系。当用户对每一台主机感兴趣的时候,他们也会对特定的服务感兴趣,因此咱们最终选择把二者相结合,成为一个组合索引,它容许以服务名称,主机,和时间戳的顺序进行有效的查找。
DAPI在谷歌的使用有三类:使利用DAPI的持续的线上Web应用,维护良好的能够在控制台上调用的基于DAPI的工具,能够被写入,运行、不过大部分已经被忘记了的一次性分析工具。咱们知道的有3个持久性的基于DAPI的应用程序,8个额外的按需定制的基于DAPI分析工具,以及使用DAPI框架构建的约15~20一次性的分析工具。在这以后的工具就这是很难说明了,由于开发者能够构建、运行和丢弃这些项目,而不须要Dapper团队的技术支持。
绝大多数用户使用发生在基于web的用户交互接口。篇幅有限,咱们不能列出每个特色,而只能把典型的用户工做流在图6中展现。
图6
为了让用户查询实时数据,Dapper的用户界面可以直接与Dapper每一台生产环境下的服务器上的守护进程进行交互。在该模式下,不可能期望能看到上面所说的系统级的图表展现,但仍然能够很容易基于性能和网络特性选取一个特定的跟踪。在这种模式下,可在几秒钟内查到实时的数据。
根据咱们的记录,大约有200个不一样的Google工程师在一天内使用的Dapper的UI;在一周的过程当中,大约有750-1000不一样的用户。这些用户数,在新功能的内部通告上,是按月连续的。一般用户会发送特定跟踪的链接,这将不可避免地在查询跟踪状况时中产生不少一次性的,持续时间较短的交互。
Dapper在Google被普遍应用,一部分直接经过Dapper的用户界面,另外一部分间接地经过对Dapper API的二次开发或者创建在基于api的应用上。在本节中,咱们并不打算罗列出每一种已知的Dapper使用方式,而是试图覆盖Dapper使用方式的“基本向量”,并努力来讲明什么样的应用是最成功的。
Google AdWords系统是围绕一个大型的关键词定位准则和相关文字广告的数据库搭建的。当新的关键字或广告被插入或修改时,它们必须经过服务策略术语的检查(如检查不恰当的语言,这个过程若是使用自动复查系统来作的话会更加有效)。
当轮到从头从新设计一个广告审查服务时,这个团队迭代的从第一个系统原型开始使用Dapper,而且,最终用Dapper一直维护着他们的系统。Dapper帮助他们从如下几个方面改进了他们的服务:
广告审查团队普遍使用了Dapper Annotation API。Guice[13]开源的AOP框架用来在重要的软件组件上标注“@Traced”。这些跟踪信息能够进一步被标注,包含:重要子路径的输入输出大小、基础信息、其余调试信息,全部这些信息将会额外发送到日志文件中。
同时,咱们也发现了一些广告审查小组在使用方面的不足。好比:他们想根据他们全部跟踪的Annotation信息,在一个交互时间段内进行搜索,然而这就必须跑一个自定义的MapReduce或进行每个跟踪的手动检查。另外,在Google还有一些其余的系统在也从通用调试日志中收集和集中信息,把那些系统的海量数据和Dapper仓库整合也是有价值的。
总的来讲,即使如此,广告审查团队仍然对Dapper的做用进行了如下评估,经过使用Dapper的跟踪平台的数据分析,他们的服务延迟性已经优化了两个数量级。
Google维护了一个从运行进程中不断收集并集中异常信息报告的服务。若是这些异常发生在Dapper跟踪采样的上下文中,那么相应的跟踪ID和span的ID也会做为元数据记录在异常报告中。异常监测服务的前端会提供一个连接,从特定的异常信息的报告直接导向到他们各自的分布式跟踪。广告审查团队使用这个功能能够了解bug发生的更大范围的上下文。经过暴露基于简单的惟一ID构建的接口,Dapper平台被集成到其余事件监测系统会相对容易。
考虑到移动部件的数量、代码库的规模、部署的范围,调试一个像全文搜索那样服务(第1节里提到过)是很是具备挑战性的。在这节,咱们描述了咱们在减轻全文搜索的延迟分布的长尾效应上作的各类努力。Dapper可以验证端到端的延迟的假设,更具体地说,Dapper可以验证对于搜索请求的关键路径。当一个系统不只涉及数个子系统,而是几十个开发团队的涉及到的系统的状况下,端到端性能较差的根本缘由到底在哪,这个问题即便是咱们最好的和最有经验的工程师也没法正确回答。在这种状况下,Dapper能够提供急需的数据,并且能够对许多重要的性能问题得出结论。
图7:全局搜索的跟踪片断,在不常遇到高网络延迟的状况下,在沿着关键路径的端到端的请求延迟,如图所示。
在调试延迟长尾效应的过程当中,工程师能够创建一个小型库,这个小型库能够根据DAPI跟踪对象来推断关键路径的层级结构。这些关键路径的结构能够被用来诊断问题,而且为全文搜索提供可优先处理的预期的性能改进。Dapper的这项工做致使了下列发现:
在任何给定的时间内,Google内部的一个典型的计算集群是一个聚集了成千上万个逻辑“任务”的主机,一套的处理器在执行一个通用的方法。Google维护着许多这样的集群,固然,事实上,咱们发如今一个集群上计算着的这些任务一般依赖于其余的集群上的任务。因为任务们之间的依赖是动态改变的,因此不可能仅从配置信息上推断出全部这些服务之间的依赖关系。不过,除了其余方面的缘由以外,在公司内部的各个流程须要准确的服务依赖关系信息,以肯定瓶颈所在,以及计划服务的迁移。Google的可称为“Service Dependencies”的项目是经过使用跟踪Annotation和DAPI MapReduce接口来实现自动化肯定服务依赖归属的。
Dapper核心组件与Dapper跟踪Annotation一并使用的状况下,“Service Dependencies”项目可以推算出任务各自之间的依赖,以及任务和其余软件组件之间的依赖。好比,全部的BigTable的操做会加上与受影响的表名称相关的标记。运用Dapper的平台,Service Dependencies团队就能够自动的推算出依赖于命名的不一样资源的服务粒度。
Google投入了大量的人力和物力资源在他的网络结构上。从前网络管理员可能只关注独立的硬件信息、经常使用工具及以及搭建出的各类全局网络鸟瞰图的dashboard上的信息。网络管理员确实能够一览整个网络的健康情况,可是,当遇到问题时,他们不多有可以准确查找网络负载的工具,用来定位应用程序级别的罪魁祸首。
虽然Dapper不是设计用来作链路级的监控的,可是咱们发现,它是很是适合去作集群之间网络活动性的应用级任务的分析。Google可以利用Dapper这个平台,创建一个不断更新的控制台,来显示集群之间最活跃的网络流量的应用级的热点。此外,使用Dapper咱们可以为昂贵的网络请求提供指出的构成缘由的跟踪,而不是面对不一样服务器之间的信息孤岛而无所适从。创建一个基于Dapper API的dashboard总共没花超过2周的时间。
在Google的许多存储系统是由多重独立复杂层级的分布式基础设备组成的。例如,Google的App Engine[5]就是搭建在一个可扩展的实体存储系统上的。该实体存储系统在基于BigTable上公开某些RDBMS功能。 BigTable的同时使用Chubby[7](分布式锁系统)及GFS。再者,像BigTable这样的系统简化了部署,并更好的利用了计算资源。
在这种分层的系统,并不老是很容易肯定最终用户资源的消费模式。例如,来自于一个给定的BigTable单元格的GFS大信息量主要来自于一个用户或是由多个用户产生,可是在GFS层面,这两种明显的使用场景是很难界定。并且,若是缺少一个像Dapper同样的工具的状况下,对共享服务的竞争可能会一样难于调试。
第5.2节中所示的Dapper的用户界面能够聚合那些调用任意公共服务的多个客户端的跟踪的性能信息。这就很容易让提供这些服务的源从多个维度给他们的用户排名。(例如,入站的网络负载,出站的网络负载,或服务请求的总时间)
对于一些“救火”任务,Dapper能够处理其中的一部分。“救火”任务在这里是指一些有风险很高的在分布式系统上的操做。一般状况下,Dapper用户当正在进行“救火”任务时须要使用新的数据,而且没有时间写新的DAPI代码或等待周期性的报告运行。
对于那些高延迟,不,可能更糟糕的那些在正常负载下都会响应超时的服务,Dapper用户界面一般会把这些延迟瓶颈的位置隔离出来。经过与Dapper守护进程的直接通讯,那些特定的高延迟的跟踪数据轻易的收集到。当出现灾难性故障时,一般是没有必要去看统计数据以肯定根本缘由,只查看示例跟踪就足够了(由于前文提到过从Dapper守护进程中几乎能够当即得到跟踪数据)。
可是,如在6.5节中描述的共享的存储服务,要求当用户活动过程当中忽然中断时能尽量快的汇总信息。对于事件发生以后,共享服务仍然能够利用汇总的的Dapper数据,可是,除非收集到的Dapper数据的批量分析能在问题出现10分钟以内完成,不然Dapper面对与共享存储服务相关的“救火”任务就很难按预想的那般顺利完成。
虽然迄今为止,咱们在Dapper上的经验已经大体符合咱们的预期,可是也出现了一些积极的方面是咱们没有充分预料到的。首先,咱们得到了超出预期的Dapper使用用例的数量,对此咱们可谓欢心鼓舞。另外,在除了几个的在第6节使用经验中提到过的一些用例以外,还包括资源核算系统,对指定的通信模式敏感的服务的检查工具,以及一种对RPC压缩策略的分析器,等等。咱们认为这些意想不到的用例必定程度上是因为咱们向开发者以一种简单的编程接口的方式开放了跟踪数据存储的缘故,这使得咱们可以充分利用这个大的多的社区的创造力。除此以外,Dapper对旧的负载的支持也比预期的要简单,只须要在程序中引入一个用新版本的从新编译过的公共组件库(包含常规的线程使用,控制流和RPC框架)便可。
Dapper在Google内部的普遍使用还为咱们在Dapper的局限性上提供了宝贵的反馈意见。下面咱们将介绍一些咱们已知的最重要的Dapper的不足:
在分布式系统跟踪领域,有一套完整的体系,一部分系统主要关注定位到故障位置,其余的目标是针对性能进行优化。 Dapper确实被用于发现系统问题,但它更一般用于探查性能不足,以及提升全面大规模的工做负载下的系统行为的理解。
与Dapper相关的黑盒监控系统,好比Project5[1],WAP5[15]和Sherlock[2],能够说不依赖运行库的状况下,黑盒监控系统可以实现更高的应用级透明。黑盒的缺点是必定程度上不够精确,并可能在统计推断关键路径时带来更大的系统损耗。
对于分布式系统监控来讲,基于Annotation的中间件或应用自身是一个多是更受欢迎的解决办法.拿Pip[14]和Webmon[16]系统举例,他们更依赖于应用级的Annotation,而X-Trace[12],Pinpoint[9]和Magpie[3]大多集中在对库和中间件的修改。Dapper更接近后者。像Pinpoint,X-Trace,和早期版本的Magpie同样,Dapper采用了全局标识符把分布式系统中各部分相关的事件联系在一块儿。和这些系统相似,Dapper尝试避免使用应用级Annotation,而是把的植入隐藏在通用组件模块内。Magpie放弃使用全局ID,仍然试图正确的完成请求的正确传播,他经过采用应用系统各自写入的事件策略,最终也能精确描述不一样事件之间关系。可是目前还不清楚Magpie在实际环境中实现透明性这些策略到底多么有效。 X-Trace的核心Annotation比Dapper更有野心一些,由于X-Trace系统对于跟踪的收集,不只在跟踪节点层面上,并且在节点内部不一样的软件层也会进行跟踪。而咱们对于组件的低性能损耗的要求迫使咱们不能采用X-Trace这样的模型,而是朝着把一个请求链接起来完整跟踪所能作到的最小代价而努力。而Dapper的跟踪仍然能够从可选的应用级Annotation中获益。
在本文中,咱们介绍Dapper这个Google的生产环境下的分布式系统跟踪平台,并汇报了咱们开发和使用它的相关经验。 Dapper几乎在部署在全部的Google系统上,并能够在不须要应用级修改的状况下进行跟踪,并且没有明显的性能影响。Dapper对于开发人员和运维团队带来的好处,能够从咱们主要的跟踪用户界面的普遍使用上看出来,另外咱们还列举了一些Dapper的使用用例来讲明Dapper的做用,这些用例有些甚至都没有Dapper开发团队参与,而是被应用的开发者开发出来的。
据咱们所知,这是第一篇汇报生产环境下分布式系统跟踪框架的论文。事实上,咱们的主要贡献源于这个事实:论文中回顾的这个系统已经运行两年之久。咱们发现,结合对开发人员提供简单API和对应用系统彻底透明来加强跟踪的这个决定,是很是值得的。
咱们相信,Dapper比之前的基于Annotation的分布式跟踪达到更高的应用透明度,这一点已经经过只须要少许人工干预的工做量得以证实。虽然必定程度上得益于咱们的系统的同质性,但它自己仍然是一个重大的挑战。最重要的是,咱们的设计提出了一些实现应用级透明性的充分条件,对此咱们但愿可以对更错杂环境下的解决方案的开发有所帮助。
最后,经过开放Dapper跟踪仓库给内部开发者,咱们促使更多的基于跟踪仓库的分析工具的产生,而仅仅由Dapper团队默默的在信息孤岛中埋头苦干的结果远达不到如今这么大的规模,这个决定促使了设计和实施的展开。