【译】Dapper, a Large-Scale Distributed Systems Tracing Infrastructure

本文翻译了论文《Dapper, a Large-Scale Distributed Systems Tracing Infrastructure》的全部内容,原论文“科学”下载。作为谷歌在分布式系统领域工程化的经典论文(2010年),对后来其他的分布式系统链路追踪提供了很多可以借鉴的地方,例如Jaeger,Opentracing,还有很多国内大厂自开发的链路追踪平台都参考了该论文的工程实现。阅读愉快,欢迎在留言区勘误和提出意见。

Dapper,一种大规模分布式系统追踪基础设施

摘要

现代互联网的服务通常会被当做复杂的大规模分布式系统来实现。 这些应用程序会由不同团队开发的软件模块集合构建而成,可能使用不同的编程语言,并且可以跨越多个物理设施的数千台机器。 在这样的环境中,有助于理解系统行为和推断性能问题的工具简直是无价之宝。

在这里,我们介绍了 Google 生产环境中分布式系统追踪基础架构 Dapper 的设计,并描述了我们的低开销、应用程序级透明和在超大规模系统上全面部署的设计目标是如何实现的。 Dapper 与其他追踪系统在概念上有相似之处,尤其是 Magpie [3] 和 X-Trace [12],但做出的某些设计选择对其在我们的环境中取得的成功至关重要,例如使用采样和将检测工具限制在相当少数量的公共库。

本文的主要目标是报告我们两年多来构建、部署和使用该系统的经验,因为 Dapper 衡量成功的首要标准是它对开发人员和运营团队的有用性。 Dapper 一开始是一个独立的追踪工具,但后来演变成一个监控平台,可以创建许多不同的工具,其中一些是其设计者没有预料到的。 我们描述了一些使用 Dapper 构建的分析工具,在 Google 中分享了有关其使用情况的统计数据,展示了一些示例用户场景,并讨论了迄今为止的经验教训。

1 简介

我们构建 Dapper 是为了向 Google 的开发人员提供有关复杂分布式系统行为的更多信息。 此类系统特别受关注,因为大量小型服务器的集合对于互联网服务工作负载而言是一种特别节约成本的平台 [4]。 在这种情况下理解系统行为需要观察跨许多不同程序和机器的相关活动。

一个网络搜索的例子将说明此类系统需要解决的一些挑战。前端服务可能会将 Web 查询分发到数百个查询服务器,每个服务器都在自己的索引片段中进行搜索。查询也可以发送到多个其他子系统,这些子系统可以处理广告、检查拼写或寻找专门的结果,包括图像、视频、新闻等。所有这些服务的结果都被选择性地组合在结果页面中;我们称这个模型为“通用搜索”[6]。总的来说,处理一个通用搜索查询可能需要数千台机器和许多不同的服务。此外,使用网络搜索的用户对延迟很敏感,这可能是由任何子系统的性能不佳造成的。只查看整体延迟的工程师可能知道存在问题,但可能无法猜测哪个服务有问题,也无法猜测它为什么表现不佳。首先,工程师可能不知道正在使用哪些服务;新的服务和功能每周都在被添加和修改,来增加用户可见的功能,并增强其他方面,如性能或安全性。其次,工程师不会是每个内部服务的领域专家;每个服务都是由不同的团队构建和维护的。第三,服务和机器可能由许多不同的客户端同时共享,因此性能问题可能是由于另一个应用程序的行为。例如,前端可以处理许多不同的请求类型,或者像 Bigtable [8] 这样的存储系统在跨多个应用程序共享时可能是最有效的。

上述场景对 Dapper 提出了两个基本要求:全面部署和持续监控。 全面很重要,因为即使系统的一小部分没有受到监控,追踪的基础设施的有用性也会受到严重影响。 此外,应该始终开启监控,因为通常情况下,异常或其他值得注意的系统行为很难或不可能重现。 这些要求产生了三个具体的设计目标:

  • 低开销:追踪系统对正在运行的服务性能影响应该可以忽略不计。 在一些高度优化的服务中,即使很小的监控开销也很容易引起注意,并可能迫使部署团队关闭追踪系统。

  • 应用程序级透明:程序员不需要知道追踪系统。 依赖于应用程序开发人员的积极协作才能运行的追踪基础设施是极其脆弱的,并且经常由于检测工具的错误或遗漏而被破坏,因此违反了普遍性的要求。 这在像我们这样快节奏的开发环境中尤为重要。

  • 可扩展性:它需要应对至少在未来几年内 Google 服务和集群规模的增长。

另一个设计目标是追踪数据在生成后可快速用于分析:最好在一分钟内。 尽管,基于数小时前的数据运行的追踪分析系统仍然非常有价值,但可用的最新信息可以更快地对生产异常做出反应。

真正的应用程序级透明,可能是我们最具挑战性的设计目标,是通过将 Dapper 的核心检测工具限制在一个全面的线程、控制流和 RPC 公共库代码的小型语料库中实现的。 使用自适应采样有助于使系统可扩展并降低性能开销,如第 4.4 节所述。 由此产生的系统还包括用于收集追踪的代码、将它们可视化的工具以及用于分析大量追踪的公共库和 API(应用程序编程接口)。 尽管 Dapper 有时足以让开发人员识别性能异常的来源,但它并不打算取代所有其他工具。 我们发现 Dapper 系统范围内的数据通常侧重于性能调查,以便其他工具可以在本地应用。

1.1 贡献总结

分布式系统追踪工具的设计领域已经在之前的多篇优秀文章中进行了探讨,其中 Pinpoint [9]、Magpie [3] 和 X-Trace [12] 与 Dapper 的关系最为密切。 这些系统往往在其开发的早期阶段就在研究文献中进行了描述,然后才有机会清楚地评估重要的设计选择。 由于 Dapper 已经在生产环境大规模地运行了很多年,我们决定将这篇论文的重点放在 Dapper 的部署教会我们什么、我们的设计决策如何发挥作用以及它在哪些方面最有用是最合适的。 Dapper 作为性能分析工具开发平台的价值,以及其本身的监控工具,是我们在回顾性评估中可以确定的少数意外结果之一。

尽管 Dapper 与 Pinpoint 和 Magpie 等系统共享其许多顶层思想,但我们的实现包含了该领域的许多新贡献。 例如,我们发现采样对于低开销是必要的,尤其是在高度优化的 Web 服务中,这些服务往往对延迟非常敏感。 也许更令人惊讶的是,我们发现只有千分之一的请求样本为追踪数据的许多常见用途提供了足够的信息。

我们系统的另一个重要特征是我们能够实现的应用程序级的透明度。 我们的检测工具被限制在软件中足够低的级别,即使是像谷歌网络搜索这样的大型分布式系统也可以在没有额外注解的情况下进行追踪。 虽然这在谷歌更容易实现,因为我们的部署环境具有一定程度的同质性,但我们这样做的结果证明了实现这种透明度水平的一些充分条件。

2 Dapper 中的分布式追踪

分布式服务的追踪架构需要记录以给定发起者作为代表的系统中完成的所有工作信息。 例如,图 1 显示了具有 5 个服务器的服务:一个前端 (A)、两个中间层(B 和 C)和两个后端(D 和 E)。 当用户请求(本例中的发起者)到达前端时,它向服务器 B 和 C 发送两个 RPC请求。B 可以立即响应,但 C 需要后端 D 和 E 的工作结果才能回复 A,即依次响应发起请求。 此请求的一个简单但有用的分布式追踪,是每个服务器发送和接收的每条消息的消息标识符和时间戳事件的集合。

图 1:代表用户请求 X 通过一个简单的服务系统所采取的路径。标有字母的节点代表分布式系统中的进程

以前已经提出了两类解决方案来聚合此信息,以便可以将所有记录条目与给定的发起者(例如,图 1 中的请求X) 相关联:黑盒和基于注解的监控方案。黑盒方案 [1] [15] [2] 假设除了上述消息记录之外没有其他信息,并使用统计回归技术来推断该关联。基于注解的方案 [3] [12] [9] [16] 依赖于应用程序或中间件来显式标记每条记录,并使用全局标识符将这些消息记录关联回原始请求。虽然黑盒方案比基于注解的方法更轻便,但由于依赖于统计推断,它们需要更多的数据才能获得足够的准确性。而基于注解的方法的主要缺点是需要用于检测的程序。在我们的环境中,由于所有应用程序都使用相同的线程模型、控制流和 RPC 系统,我们发现可以将检测工具限制为一小组公共库,并实现对应用程序开发人员有效透明的监控系统。

我们倾向于将 Dapper 追踪视为嵌套的 RPC 树。 然而,我们的核心数据模型并不局限于我们特定的 RPC 框架; 我们还追踪 Gmail 中的 SMTP 会话、来自外部世界的 HTTP 请求以及对 SQL 服务器的出站查询等活动。 言回正传,我们使用树、跨度和注解对 Dapper 追踪进行建模。

2.1 追踪树和跨度

在 Dapper 追踪树中,树节点是我们称为跨度 spans 的基本工作单元。 边表示跨度与其父跨度之间的因果关系。 然而,除了它在更大的追踪树中的位置,跨度本身也是一个带时间戳记录的简单日志,这些记录编码了跨度的开始和结束时间、任何 RPC 请求的时间数据以及零个或多个特定应用程序的注解(如第 2.3 节所述)。

我们在图 2 中说明了跨度如何形成更大追踪的结构。 Dapper 为每个跨度记录了一个人类可读的跨度名称,以及跨度 ID 和父 ID,以便在一条分布式追踪中重建单个跨度之间的因果关系。 在没有父 ID 的情况下创建的跨度称为根跨度。 与特定追踪关联的所有跨度也共享一个公共追踪 ID(图中未显示)。 所有这些 ID 都是概率上唯一的 64 位整数。 在典型的 Dapper 追踪中,我们希望为每个 RPC 找到一个跨度,并且基础设施的每个附加层都会为追踪树增加一个额外的深度级别。

图 2:Dapper 追踪树中五个跨度 spans 之间的因果关系和时间关系

图 3 提供了典型 Dapper 追踪跨度中记录事件的一个更详细的视图。 这个特定的跨度描述了图 2 中两个“Helper.Call” RPC 的整段时间。跨度开始和结束时间以及所有 RPC 时间信息都由 Dapper 的 RPC 公共库检测工具记录。 如果应用程序所有者选择使用他们自己的注解(如图中的“foo”注解)来扩充追踪,这些也会与其余的跨度数据一起记录。

图 3:图 2 中单个跨度的详细视图

需要注意的是,一个跨度可以包含来自多个主机的信息; 事实上,每个 RPC 跨度都包含来自客户端和服务器进程的注解,这使得双主机跨度成为最常见的跨度。 由于客户端和服务器上的时间戳来自不同的主机,我们必须注意时钟偏差。 在我们的分析工具中,我们利用了这样一个事实,即 RPC 客户端总是在服务器收到请求之前发送请求,反之亦然。 这样,我们就有了 RPC 服务端跨度时间戳的下限和上限。

2.2 检测点

Dapper 几乎完全依赖于少数一些公共库的检测,从而能够在应用程序开发人员几乎零干预的情况下遵循分布式控制路径:

  • 当线程处理追踪的控制路径时,Dapper 将追踪上下文附加到线程本地的存储。 追踪上下文是一个小型且易于复制的跨度属性容器,包括了例如追踪ID和跨度 ID。

  • 当计算延迟或异步时,大多数 Google 开发人员使用通用控制流库来构造回调并将它们安排在线程池或其他执行器中。Dapper 确保所有此类回调都存储其创建者的追踪上下文,并且在调用回调时,此追踪上下文与适当的线程相关联。 通过这种方式,用于追踪重建的 Dapper ID 能够透明地遵循异步控制路径。

  • 谷歌几乎所有的进程间通信都是围绕一个单一的 RPC 框架构建的,并绑定在 C++ 和 Java 中。 我们已经对该框架进行了检测,以围绕所有的 RPC 定义跨度。 对于追踪的 RPC ,span 和 trace id 从客户端传输到服务器。 在 Google ,像那些基于 RPC 的系统被广泛使用,所以这是一个必不可少的检测点。 我们计划在非RPC 通信框架发展对其进行检测,并找到相应的用户群体。

Dapper 追踪数据与语言无关,并且生产中的许多追踪将来自用 C++ 和 Java 编写的进程的数据组合在一起。 在 3.2 节中,我们讨论了在实践中能够实现的应用程序透明度级别。

2.3 注解

上述检测点足以得出复杂分布式系统的详细追踪,并且使 Dapper 核心功能可用于其他未经修改的 Google 应用程序。 但是,Dapper 还允许应用程序开发人员使用附加信息来丰富 Dapper 追踪,这些信息可能有助于监视更高级别的系统行为或帮助调试问题。 我们允许用户通过一个简单的 API 定义带时间戳的注解,其核心如图 4 所示。这些注解可以具有任意内容。 为了保护 Dapper 用户免受意外过度大量的日志记录,单个追踪跨度对其总注解量有一个可配置的上限。 无论应用程序行为如何,应用程序级别的注解都无法替换结构跨度或 RPC 信息。

1
2
3
4
5
6
// C++: 
const string& request = ...;
if (HitCache())
TRACEPRINTF("cache hit for %s", request.c_str());
else
TRACEPRINTF("cache miss for %s", request.c_str());
1
2
3
4
5
6
7
// Java: 
Tracer t = Tracer.getCurrentTracer();
String request = ...;
if (hitCache())
t.record("cache hit for " + request);
else
t.record("cache miss for " + request);

图 4:C++ 和 Java 中 Dapper 注解 API 的常见情况使用模式。

除了简单的文本注解之外,Dapper 还支持键值对映射的注解,这为开发人员提供了更多的追踪能力,例如维护计数器、记录二进制消息以及传输任意用户定义的数据以及进程内的追踪请求。 这些键值对注解用于在分布式追踪上下文中定义特定于应用程序的等价类。

2.4 采样

低开销是 Dapper 的一个关键设计目标,因为如果它对性能有任何明显影响的话,服务运营者将不愿意部署尚未证其价值的新工具,这是可以理解的。 此外,我们希望允许开发人员使用注解 API 而不必担心额外的开销。 我们还发现某些类别的 Web 服务确实对检测开销很敏感。 因此,除了使 Dapper 收集的基本检测开销尽可能小之外,我们还通过仅记录所有追踪的一小部分来进一步控制开销。 我们将在 4.4 节中更详细地讨论这种追踪采样方案。

2.5 追踪收集

Dapper 追踪的日志记录和收集管道是一个三阶段过程(参见图 5)。 首先,跨度数据被写入 (1) 到本地日志文件。 然后 (2) 由 Dapper 守护进程和用于收集数据的基础架构从所有生产环境主机中提取并最终写入 (3) 到某一个区域的 Dapper Bigtable [8] 存储库的单元。 追踪被布置为单个 Bigtable 行,每列对应一个跨度。 Bigtable 对稀疏表布局的支持在这里很有用,因为单个追踪可以具有任意数量的跨度。 追踪数据收集的中位延迟(即数据从检测的应用程序二进制文件传播到中央存储库所需的时间)不到 15 秒。98分位延迟随时间的分布本身是双峰的; 大约 75% 的时间里, 98 分位的收集延迟小于两分钟,但其他大约 25% 的时间里延迟可以增长到数个小时。

图 5:Dapper 收集管道概述

Dapper 还提供了一个 API 来简化对我们存储库中追踪数据的访问。 Google 的开发人员使用此 API 来构建通用和特定于应用程序的分析工具。 第 5.1 节包含有关其使用的更多信息。

2.5.1 带外追踪收集

Dapper 系统使用请求树本身执行追踪日志记录和带外收集。 这样做有两个不相关的原因。 首先,带内收集方案(追踪数据在 RPC 响应标头内发回)会影响应用程序网络动态。 在 Google 的许多大型系统中,找到具有数千个跨度的追踪情况并不少见。 然而,RPC 响应——即使在如此大的分布式追踪的根部附近——仍然可能相对较小:通常小于 10 KB。 在这种情况下,带内 Dapper 追踪数据会使应用程序数据相形见绌,并使后续分析的结果产生偏差。 其次,带内收集方案假设所有 RPC 都是完美嵌套的。 我们发现有许多中间件系统在它们自己的所有后端都返回最终结果之前,就会将结果返回给它们的调用者。 带内收集系统无法表示这种非嵌套分布式执行模式。

2.6 安全和隐私考虑

记录一些 RPC 负载信息将丰富 Dapper 追踪,因为分析工具可能能够在负载数据中找到可以解释性能异常的模式。 但是,在某些情况下,有效载荷数据可能包含不应向未经授权的内部用户披露的信息,包括从事性能调试的工程师。

由于安全和隐私问题是不可协商的,Dapper 存储了 RPC 方法的名称,但此时不记录任何有效负载数据。 相反,应用程序级注解提供了一种方便的选择加入机制:应用程序开发人员可以选择将其确定对以后分析有用的任何数据与跨度相关联。

Dapper 还提供了一些设计者没有预料到的安全优势。 例如,通过追踪公共安全协议参数,Dapper 用于监控应用程序是否通过适当级别的身份验证或加密来满足安全策略。 Dapper 还可以提供信息以确保按预期实施基于策略的系统隔离,例如承载敏感数据的应用程序不会与未经授权的系统组件交互。 这些类型的测量提供了比源代码审计更大的保证。

3 Dapper 部署状态

两年多来,Dapper 一直是我们的生产环境的追踪系统。 在本节中,我们将报告系统的状态,重点关注它在多大程度上满足了我们全面部署和应用程序级透明的目标。

3.1 Dapper 运行时公共库

也许 Dapper 代码库中最关键的部分是基本 RPC、线程和控制流库的检测,其中包括跨度创建、采样和记录到本地磁盘。 除了轻量级之外,此代码还需要稳定和健壮,因为它链接到大量应用程序中,这使得维护和错误修复变得困难。 核心检测在 C++ 中不到 1000 行代码,在 Java 中不到 800 行。 键值对注解的实现增加了额外的 500 行代码。

3.2 生产覆盖

可以从两个维度评估 Dapper 渗透率:可以生成 Dapper 追踪的生产进程的比例(即与 Dapper 检测的运行时库链接的那些)和运行 Dapper 追踪收集守护程序的生产机器的比例。 Dapper 的守护进程是我们虚机基本镜像的一部分,使其几乎出现在 Google 的每台服务器上。 很难确定 Dapper 就绪的进程的精确比例,因为 Dapper 看不到不生成追踪信息的进程。 然而,考虑到 Dapper 检测库无处不在,我们估计几乎每个 Google 生产进程都支持追踪。

在某些情况下,Dapper 无法正确遵循控制路径。 这些通常源于使用非标准控制流原语,或者当 Dapper 错误地将因果关系归因于不相关的事件时。 Dapper 提供了一个简单的库来帮助开发人员手动控制追踪传播作为一种变通方法。 目前有 40 个 C++ 应用程序和 33 个 Java 应用程序需要一些手动追踪传播,对应于总数为数千的一小部分。 还有极少数程序使用未检测的通信库(例如,原始 TCP 套接字或 SOAP RPC),因此不支持 Dapper 追踪。 如果认为重要,可以将 Dapper 支持添加到这些应用程序中。

作为生产安全措施,可以关闭 Dapper 追踪。 事实上,它在早期默认是关闭的,直到我们对其稳定性和低开销建立了充足的信心。 Dapper 团队不定期进行审计,以查找服务所有者关闭追踪的配置更改。 此类更改很少见,通常源于对监控开销的担忧。 迄今为止,所有这些变化都已恢复在进一步调查和衡量实际开销后,而且这是无形的变动。

3.3 追踪注解的使用

程序员倾向于使用特定于应用程序的注解作为一种分布式调试日志文件,或者通过某些特定于应用程序的功能对追踪进行分类。 例如,所有 Bigtable 请求都使用正在访问的表的名称进行注解。 目前,所有 Dapper 跨度的 70% 和所有 Dapper 追踪的 90% 至少有一个应用程序指定的注解。

41 个 Java 和 68 个 C++ 应用程序添加了自定义应用程序注解,以便更好地了解其服务中的跨度内活动。 值得注意的是,到目前为止,我们采用注解 API 的 Java 开发人员在每个 span 中所做的注解比他们的 C++ 同行多。 这可能是因为我们的 Java 工作负载往往更接近最终用户; 这些类型的应用程序通常处理更广泛的请求组合,因此具有相对复杂的控制路径。

4 管理追踪开销

由于追踪生成和收集开销,以及存储和分析追踪数据所需的资源,追踪系统的成本会被视为被监控系统的性能下降。 尽管有人可能会争辩说,有价值的追踪基础设施可能值得性能损失,但我们相信,如果基线开销可以明显地可以忽略不计,那么最初的采用将得到极大的促进。

在本节中,我们将介绍主要 Dapper 检测操作的开销、追踪收集的开销以及 Dapper 对生产系统工作负载的影响。 我们还描述了 Dapper 的自适应追踪采样机制如何帮助我们平衡对低开销的需求和对代表性追踪的需求。

4.1 追踪生成的开销

追踪生成的开销是 Dapper 性能足迹中最关键的部分,因为在紧急情况下可以更轻松地关闭收集和分析。 Dapper 运行时库中追踪生成的开销的最重要来源是创建和销毁跨度和注解,并将它们记录到本地磁盘以供后续收集。 根跨度的创建和销毁平均需要 204 纳秒,而非根跨度的相同操作需要 176 纳秒。 不同之处在于为根跨度分配全局唯一追踪 ID 的额外成本。

如果 span 没有采样用于追踪,额外的 span 注解的成本几乎可以忽略不计,包括 Dapper 运行时中的线程本地查找,平均约 9 纳秒。 如果它被采样,用字符串文字注解追踪(很像图 4 中显示的那样)平均花费 40 纳秒。 这些测量是在 2.2GHz x86 服务器上进行的。

写入本地磁盘是 Dapper 运行时库中最昂贵的操作,但它们的可见开销大大减少,因为每个磁盘合并多个日志文件的写入操作,并相对于被追踪的应用程序异步执行。 然而,日志写入活动会对高吞吐量应用程序性能产生明显的影响,尤其是在所有请求都被追踪的情况下。 我们在第 4.3 节中量化了 Web 搜索工作负载中的这种开销。

4.2 追踪收集的开销

读取本地追踪数据也会干扰被监控的前台工作负载。 表 1 显示了基于非现实的重负载测试基准的 Dapper 守护进程在最坏情况下的 CPU 使用率。 守护进程在收集过程中永远不会使用超过 0.3% 的生产机器的一个核心,并且内存占用非常小(在堆碎片的噪音范围内)。 我们还将 Dapper 守护进程限制在内核调度程序中可能的最低优先级,以防在负载较重的主机中出现 CPU 竞争。

表 1:负载测试期间 Dapper 守护程序的 CPU 资源使用情况

Dapper 也是网络资源的轻量级消费者,我们存储库中的每个跨度平均仅对应 426 个字节。 作为我们正在监控的应用程序中网络活动的一小部分,Dapper 追踪数据的收集只使用 Google 生产环境中不到 0.01% 的网络流量。

4.3 对生产工作负载的影响

为每个请求使用大量机器的高吞吐量在线服务是最需要有效追踪的; 它们往往会生成最大量的追踪数据,同时它们对性能干扰也最敏感。 在表 2 中,我们使用我们的网络搜索集群作为此类服务的示例;我们改变采样追踪的比率,然后测量 Dapper 对平均延迟和吞吐量的性能影响。

表 2:不同的 (非自适应) Dapper 采样频率对 Web 搜索集群的延迟和吞吐量的影响。 这些延迟和吞吐量测量的实验误差分别为 2.5% 和 0.15%。

我们看到,虽然对吞吐量的影响不是很明显,但为了避免明显的延迟下降,追踪采样确实是必要的。 然而,与小于 1/16 的采样频率相关的延迟和吞吐量损失都在实验误差范围内。 在实践中,我们发现在使用低至 1/1024 的采样率时,对于大容量服务仍然有足够数量的追踪数据。 将 Dapper 的基线开销保持在极低的水平很重要,因为它为应用程序提供了完整的注解 API 而不必担心性能损失。 使用较低的采样频率有一个额外的好处,即允许数据在被垃圾收集之前在主机的本地磁盘上保留更长时间,这为收集基础设施提供了更大的灵活性。

4.4 自适应采样

任何给定进程的 Dapper 开销与每单位时间处理样本的追踪数量成正比。 Dapper 的第一个生产版本对 Google 的所有进程使用统一的采样频率,平均每 1024 个候选追踪进行一个采样。 这个简单的方案对我们的高吞吐量在线服务很有效,因为绝大多数感兴趣的事件仍然很可能经常出现以被捕获。

然而,较低流量的工作负载以如此低的采样率可能会错过重要事件,而又可以用可接受的性能开销容忍更高的采样率。 此类系统的解决方案是覆盖默认采样率,这又需要我们实施在 Dapper 中试图避免的那种手动干预。

我们正在部署一种自适应采样方案,该方案不是由统一的采样频率参数化,而是由每单位时间的追踪采样的期望速率进行参数化。 这样,流量低的工作负载会自动提高采样率,而流量非常高的工作负载会降低采样率,从而控制开销。 使用的实际采样率与追踪本身一起记录; 这有助于在围绕 Dapper 数据构建的分析工具中准确计算追踪频率。

4.5 应对激进的采样

新 Dapper 用户经常想知道低采样率(对于高流量服务通常低至 0.01%)是否会干扰他们的分析。 我们在 Google 的经验使我们相信,对于高吞吐量服务,激进的抽样不会妨碍最重要的分析。 如果一个显著的执行模式在这样的系统中出现一次,它将出现数千次。 容量较小的服务(也许每秒几十个而不是几万个请求)可以追踪每个请求; 这就是促使我们决定转向自适应采样率的原因。

4.6 收集过程中的额外采样

上述采样机制旨在最大限度地减少包含 Dapper 运行时库的应用程序中可感知的开销。 不过,Dapper 团队还需要控制写入其中央存储库的数据总大小,因此我们为此目的合并了第二轮抽样。

我们的生产集群目前每天生成超过 1 TB 的采样追踪数据。 Dapper 用户希望追踪数据在最初从生产进程中记录后至少保存可用两周。 然后必须权衡增加追踪数据密度的好处与 Dapper 存储库的机器和磁盘存储成本。 对大部分请求进行采样也会使 Dapper 收集器接近 Dapper Bigtable 存储库的写入吞吐量限制。

为了在资源要求和累积的 Bigtable 写入吞吐量方面保持灵活性,我们在收集系统本身中添加了对额外采样的支持。 我们利用这样一个事实,即给定追踪的所有跨度(尽管它们可能分布在数千台不同的主机上)共享一个公共追踪 ID。 对于收集系统中看到的每个跨度,我们将关联的追踪 ID 散列为标量 z,其中 0 ≤ z ≤ 1。如果 z 小于我们的收集采样系数,我们保留跨度并将其写入 Bigtable。 否则,我们将其丢弃。 通过根据我们的采样决定的追踪 id,我们要么对整个追踪进行采样,要么丢弃整个追踪,而不是追踪内的单个跨度。 我们发现这个额外的配置参数使我们的收集管道的管理变得更加简单,因为我们可以通过更改配置文件中的单个参数轻松调整我们的全局写入速率。

如果整个追踪和收集系统只有一个采样参数会更简单,但是在所有部署的二进制文件中快速调整运行时采样配置是不可行的。 我们选择了一个运行时采样率,它产生的数据比我们可以写入存储库的数据略多,我们用收集系统中的二次采样系数来限制写入率。 Dapper 管道维护更容易,因为我们可以通过对二级采样配置的一次更改立即增加或减少我们的全球覆盖率和写入率。

5 通用 Dapper 工具

几年前,当 Dapper 还是一个原型的时候,只有在 Dapper 开发人员的耐心帮助下才能使用。 从那时起,我们迭代地构建了收集的基础设施、编程接口和基于 Web 的交互式用户界面,以帮助 Dapper 用户独立解决他们的问题。 在本节中,我们总结了哪些方法有效,哪些方法无效,并提供了有关这些通用分析工具的基本使用信息。

5.1 Dapper仓库API

Dapper “Depot API” 或 DAPI 提供对区域 Dapper 存储库(或“Depot”)中分布式追踪记录的直接访问。 DAPI 和 Dapper 追踪存储库是串联设计的,DAPI 旨在向这些 Dapper 存储库中包含的原始数据公开一个干净直观的界面。 我们的用例建议通过以下三种方式访问追踪数据:

通过追踪 ID 访问:DAPI 可以根据其全球唯一的追踪 ID 按需加载任何追踪。

批量访问:DAPI 可以利用 MapReduce 提供对数十亿 Dapper 追踪的并行访问。 用户重写一个接受 Dapper 追踪作为其唯一参数的虚拟函数,并且框架将在用户指定的时间窗口内为每个收集的追踪调用该函数一次。

索引访问:Dapper 存储库支持单个索引,该索引已被选择以匹配我们的常见访问模式。 该索引从通常请求的追踪特征(如下所述)映射到不同的 dapper 追踪。 由于追踪 ID 是伪随机分配的,因此这是快速访问与特定服务或主机关联的追踪的最佳方式。

所有三种访问模式都将用户引导到不同的 Dapper 追踪记录。 正如前面第 2.1 节所述,Dapper 追踪被建模为追踪跨度树,因此追踪数据结构是单个跨度结构的简单可遍历树。 跨度通常对应于 RPC 调用,并且在这些情况下,可以使用 RPC 时间信息。 时间戳应用注解也可以通过跨度结构访问。

选择合适的自定义索引是 DAPI 设计中最具挑战性的方面。 索引追踪数据所需的压缩存储仅比实际追踪数据本身少 26%,因此成本很高。 最初,我们部署了两个索引:一个用于主机的索引,一个用于服务名称。 然而,我们没有发现足够的兴趣对基于机器的索引来证明它们的存储成本是合理的。 当用户对单个机器感兴趣时,他们也对特定服务感兴趣,因此我们最终将两者组合成一个复合索引,以便按服务名称、主机和时间戳的顺序进行高效查找。

5.1.1 Google 内部的 DAPI 使用

Google 将 DAPI 使用分为三类:使用 DAPI 的持久性在线 Web 应用程序、可从命令行按需运行的维护良好的基于 DAPI 的工具,以及一次性分析工具,它们编写、运行、并且大多被遗忘。 我们分别知道 3 个基于 DAPI 的持久应用程序、8 个额外的基于 DAPI 的按需分析工具,以及大约 15-20 个使用 DAPI 框架构建的一次性分析工具。 后一类工具很难解释,因为开发人员可以在没有 Dapper 团队知识的情况下构建、运行和放弃这些项目。

5.2 Dapper 用户接口

大多数 Dapper 使用发生在基于 Web 的交互式用户界面中。 空间考虑不允许我们展示其中的每个功能,但典型的用户工作流程如图 6 所示。

图 6:通用 Dapper 用户界面中的典型用户工作流程

1:用户描述他们感兴趣的服务和时间窗口,以及他们需要区分追踪模式的任何信息(在这种情况下,跨度名称)。 他们还指定了与调查最相关的成本指标(在本例中为服务延迟)。

2:显示与给定服务关联的所有分布式执行模式的性能摘要的表格。 用户可以按照他们的意愿对这些执行模式进行排序,并选择一个进行更详细的查看。

3:一旦选择了单个分布式执行模式,就会向用户呈现所述执行模式的图形描述。 正在检查的服务在图的中心突出显示。

4:在创建与步骤 1 中选择的成本度量空间的细分相对应的桶后,Dapper 用户界面在该度量空间上呈现一个简单的频率直方图。 因此,在此示例中,我们可以看到所选执行模式的延迟大致呈对数正态分布。 还向用户呈现落入直方图的不同范围的特定示例轨迹的列表。 在这种情况下,用户单击第二个示例追踪,这会将他们带到 Dapper 用户界面中的追踪检查视图。

5:许多(如果不是大多数)Dapper 用户最终的目标是检查特定的追踪,希望收集有关系统行为根本原因的信息。 我们没有足够的空间来进行追踪视图的判断,但它的特点是全局时间线(见顶部)以及交互式展开和折叠子树的能力。 分布式追踪树的连续层由嵌套的彩色矩形表示。 每个 RPC 跨度进一步细分为在服务器进程中花费的时间(绿色)和在网络上花费的时间(蓝色)。 此屏幕截图中未显示用户注解,但可以逐个跨度地将它们选择性地包含在全局时间线中。

对于寻求实时数据的用户,Dapper 用户界面能够直接与每台生产机器上的 Dapper 守护程序通信。 在该模式下,无法查看如上所示的系统级图表,但仍然可以根据延迟或网络特征轻松选择单个追踪。 在这种操作模式下,数据可在几秒钟内实时获得。

根据我们的日志,大约有 200 名不同的 Google 工程师会在工作日使用 Dapper UI; 因此,在一周内,大约有 750-1000 个不同的用户。 这些数字与内部公告的新功能按月取模数量一致。 用户发送指向感兴趣的特定追踪的链接是很常见的,这将不可避免地在追踪检查器中产生大量一次性的、短期的流量。

6 经验

Dapper 在 Google 被广泛使用,包括直接通过 Dapper 用户界面使用和间接通过调用 API 或构建在这些 API 之上的应用程序去使用。 在本节中,我们不会尝试对 Dapper 所有已知的用途进行分类,而是尝试涵盖 Dapper 使用的“基本方向”,以说明哪些类型的应用程序最成功。

6.1 在开发过程中使用 Dapper

Google AdWords 系统是围绕一个包含关键字定位条件和相关文本广告的大型数据库而构建的。 当插入或修改新的关键字或广告时,必须检查它们是否符合服务政策条款(例如不当的语言); 通过自动审核系统提高效率的流程。

当需要从头开始重新设计 Ads Review 的一项服务时,该团队从第一个系统原型到发布和最终维护他们的系统都反复使用 Dapper。 Dapper 通过以下方式帮助他们改善服务

性能:开发人员根据请求延迟目标追踪进度并确定简单的优化机会。 Dapper 还用于识别关键路径上不必要的串行请求(通常源自开发人员没有自己编写的子系统)并提示团队随后修复它们。

正确性:Ads Review 服务围绕一个大型数据库系统展开。 该系统同时具有只读副本(廉价访问)和读写主服务器(昂贵访问)。 Dapper 用于识别一些不必要地向主服务器而不是副本发出查询的情况。 现在可以找到直接访问 master 的情况并保证重要的系统不变量。

理解:Ads Review 查询分布在多种类型的系统中,包括 BigTable、上面提到的数据库、多维索引服务以及各种其他 C++ 和 Java 后端服务。 Dapper 追踪用于评估总查询成本,并促使人们重新设计以最大限度地减少其系统依赖项的负载。

测试:新代码发布通过 Dapper 追踪 QA 流程,验证正确的系统行为和性能。 使用此过程在 Ads Review 代码本身和支持库中发现了许多问题。

Ads Review 团队广泛使用了 Dapper 注解 API。 Guice[13] 开源 AOP 框架用于将重要的软件组件标记为“@Traced”。 追踪进一步用有关重要子例程的输入和输出大小的信息、状态消息和其他调试信息进行注解,否则这些信息将被发送到日志文件。

在某些方面 Dapper 达不到 Ads Review团队的要求。 例如,他们希望在交互时间内搜索所有追踪注解,但必须运行自定义 MapReduce 或手动检查单个追踪。 此外,谷歌还有其他系统从通用调试日志中收集和集中信息,从这些系统和 Dapper 存储库集成大量数据并非易事。

不过,总体而言,Ads Review 团队估计,使用从 Dapper 追踪平台收集的数据,他们的延迟数字已改善了两个数量级。

6.1.1 与异常监控的集成

Google 维护着一项服务,该服务不断收集和集中来自正在运行的进程的异常报告。 如果这些异常发生在采样的 Dapper 追踪的上下文中,则适当的追踪和跨度 ID 将作为元数据包含在异常报告中。 然后,异常监视服务的前端提供从特定异常报告到其各自分布式追踪的链接。 Ads Review 团队使用此功能来在更大范围的取证上下文中了解异常监控服务识别出的错误。 通过围绕唯一 id 构建的导出接口,Dapper 平台可以相对轻松地集成到其他事件监控系统中。

6.2 解决长尾延迟问题

由于活动组件的数量以及代码库和部署的大小和范围,调试像通用搜索那样的服务(如第 1 节中前面所述)非常具有挑战性。 在这里,我们描述了为减弱通用搜索的长尾延迟分布所做的努力。 Dapper 能够验证关于端到端延迟的假设,更具体地说,是通用搜索请求的关键路径。 当系统不仅涉及数十个子系统而且涉及数十个工程团队时,即使是我们最优秀、最有经验的工程师也经常会错误地猜测端到端性能不佳的根本原因。 在这种情况下,Dapper 可以提供急需的事实,并能够最终回答许多重要的性能问题。

一位从事长尾延迟调试的工程师构建了一个小型库,该库可从 DAPI 追踪对象推断关键路径的层级。 然后使用这些关键路径结构来诊断问题并优先考虑通用搜索的预期性能改进。 这项与 Dapper 的合作产生了以下发现:

  • 沿关键路径的网络性能暂时下降不会影响系统吞吐量,但会对异常延迟产生深远影响。 如图 7 所示,大多数慢速的通用搜索追踪在其关键路径上都经历了网络退化。

图 7:通用搜索追踪在其关键路径某处遇到异常高网络延迟的部分,显示为端到端请求延迟的函数

  • 服务之间的意外交互导致了许多有问题且代价高昂的查询模式。 一旦被识别,它们通常很容易被纠正,但在 Dapper 之前识别这些问题本身是困难的。

  • 常见查询是从 Dapper 之外的安全日志存储库中获取的,并使用 Dapper 的唯一追踪 ID 与 Dapper 存储库连接。 然后使用此映射来构建示例查询列表,这些查询对于通用搜索中的每个单独子系统都很慢。

6.3 推断服务依赖

在任何时候,谷歌的一个典型计算集群都承载着数千个逻辑“作业”; 一组执行共同功能的进程。 当然,Google 维护着许多这样的集群,而且我们确实发现一个计算集群中的作业通常依赖于其他集群中的作业。 由于作业之间的依赖关系是动态变化的,因此不可能仅通过配置信息来推断所有服务间的依赖关系。 尽管如此,公司内的各种流程都需要准确的服务依赖信息,以便识别瓶颈并规划服务移动等。 谷歌恰当命名的“服务依赖”项目利用了追踪注解和 DAPI MapReduce 接口来自动确定服务依赖关系。

使用 Dapper 的核心检测和 Dapper 追踪注解,服务依赖项目能够推断单个作业之间的依赖关系,以及这些作业使用的共享软件基础设施的依赖关系。 例如,所有 Bigtable 操作都标有受影响表的名称。 使用 Dapper 平台,服务依赖团队因此能够以各种服务粒度自动推断对命名资源的依赖关系。

6.4 不同服务的网络使用情况

谷歌为其网络结构投入了大量的人力和物力资源。 毫不奇怪,网络运营商长期以来一直可以访问来自单个硬件的监控信息,并且构建了自定义工具和仪表板以提供全球网络利用率的鸟瞰图。 网络运营商对我们广域网的整体健康状况有合理的了解,但是,当出现问题时,他们几乎没有工具可以将网络负载正确地归因于应用程序级别的罪魁祸首。

尽管 Dapper 不是为链路级监控而设计的,但我们发现它非常适合集群间网络活动的应用程序级分析任务。 Google 能够利用 Dapper 平台构建一个持续更新的控制台,显示集群间网络流量最活跃的应用程序级端点。 此外,使用 Dapper,我们能够为这些昂贵的网络请求指向因果追踪根,而不是将我们自己限制在两个隔离的对等机器上。仪表盘页面建立在 Dapper API 之上在不到 2 周的时间内。

6.5 分层共享存储系统

Google 的许多存储系统由多个独立复杂的分布式基础架构层组成。 例如,Google App Engine[5] 建立在可扩展的实体存储系统之上。 此实体存储系统在底层 BigTable 之上公开某些 RDBMS 功能。 Bigtable 反过来使用 Chubby[7](分布式锁系统)和 GFS。 此外,BigTable 等系统作为共享服务进行管理,以简化部署并更好地利用计算资源。

在这种分层系统中,确定最终用户资源消耗模式并不总是那么容易。 例如,来自给定 BigTable 单元的大量 GFS 流量可能主要来自一个用户或多个用户,而在 GFS 级别,这两种不同使用模式之间的差异被掩盖了。 此外,在没有 Dapper 等工具的情况下,对此类共享服务的争用同样难以调试。

第 5.2 节中显示的 Dapper 用户界面可以跨任何共享服务的各种客户端对追踪性能信息进行分组和聚合。 这使得共享服务的所有者可以轻松地根据各种指标(例如,入站网络负载、出站网络负载或服务请求所花费的总时间)对其用户进行排名。

6.6 救火处理

Dapper 对一些但不是所有的消防任务很有用。 这里的“消防”是指代表处于危险中的分布式系统执行的活动。 通常,正在救火的 Dapper 用户需要访问新数据,而没有时间编写新的 DAPI 代码或等待定期报告运行。

对于遇到高延迟或更糟糕的是,在正常工作负载下超时的服务,Dapper 用户界面通常可以隔离延迟瓶颈的位置。 通过直接与 Dapper 守护进程通信,可以毫无困难地收集有关特定高延迟追踪的新数据。 在灾难性故障期间,通常不需要查看汇总统计数据来确定根本原因,并且示例追踪就足够了。

但是,像第 6.5 节中描述的共享存储服务需要在用户活动突然激增期间尽快聚合信息。 对于事件事后分析,共享服务仍然可以使用聚合的 Dapper 数据,但是在事件发生后 10 分钟内完成对收集的 Dapper 数据的批量分析之前,Dapper 将不会像它在共享存储服务的消防问题上那样有用。

7 其他经验教训

到目前为止我们对 Dapper 的体验总体上达到了我们的预期,但仍有一些我们没有完全预料到的积极方面。 我们对意外发现的用户场景的数量特别满意。 除了第 6 节中描述的一些经验之外,这些还包括资源统计系统、检查敏感服务是否符合指定通信模式的工具以及对 RPC 压缩策略的分析等。 我们将这些意外用户场景部分归因于通过简单的编程接口向开发人员开放我们的追踪数据存储的决定,因为这使我们能够利用更大社区的创造力。 为传统的工作负载添加 Dapper 支持也比预期更简单,对于那些使用通用支持的线程、控制流和 RPC 框架的程序,只需使用现有库的新版本重新编译,。

Dapper 在 Google 中的广泛使用也为我们提供了有关其某些局限性的宝贵反馈。 下面我们将描述迄今为止确定的那些最重要的。

合并效应:我们的模型隐含地假设各种子系统将一次为一个追踪请求执行工作。 在某些情况下,一次性对一组请求执行操作之前缓冲一些请求会更高效(磁盘写入的合并就是一个这样的例子)。 在这种情况下,可以将一个看似庞大的工作单元归咎于一个被追踪的请求。 此外,如果多个被追踪的请求被批处理在一起,由于我们依赖于每个追踪的单个唯一追踪 ID,因此只有其中一个会出现对跨度负责。 我们正在考虑可以识别这些情况并记录消除歧义所需的最少信息量的解决方案。

追踪批处理工作负载:Dapper 的设计针对在线服务系统,最初的目标是了解用户向 Google 发出请求所导致的系统行为。 然而,离线的数据密集型工作负载,例如那些适合 MapReduce [10] 模型的工作负载,也可以从更好的性能洞察中受益。 在这种情况下,我们需要将追踪 ID 与其他一些有意义的工作单元相关联,例如输入数据中的键(或键范围)或 MapReduce 分片。

查找根本原因:Dapper 可以有效地确定系统的哪个部分出现减速,但并不总是足以找到根本原因。 例如,一个请求可能慢不是因为它自己的行为,而是因为其他请求排在它前面。 程序可以利用应用程序级别的注解将队列大小或过载情况中继到追踪系统。 此外,如果这种影响很常见,ProfileMe [11] 中提出的配对采样技术可能会被证明是有用的。 它包括采样两次重叠的请求并观察它们在整个系统中的相对延迟。

记录内核级信息:有关内核可见事件的详细信息有时对确定根本原因很有用。 我们有许多能够追踪或以其他方式分析内核执行的工具,但是将这些信息与驻留在用户级别的追踪上下文联系起来很难以一般且不显眼的方式完成。 我们正在研究一种可能的折衷解决方案,在该解决方案中,我们从用户级获取一些内核级活动参数的快照,并将它们与活动跨度相关联。

8 相关工作

在分布式系统追踪领域有很多有益的的工作,一些系统主要专注于查明故障,而其他系统则旨在性能优化。 Dapper 已被用于故障发现,但它通常在发现性能问题和提高对大型复杂工作负载行为的一般理解方面更有用。

Dapper 与黑盒监控系统有关,例如 Project5 [1]、WAP5 [15] 和 Sherlock 系统 [2],他们声称通过不依赖运行时库,就可以实现更高程度的应用程序级透明度检测工具。 黑盒方案的缺点是在因果路径的统计推断中存在一定程度的不精确性和可能更大的开销。

中间件或应用程序本身的基于注解的显式检测可能是一种更流行的分布式系统监控方法。 Pip [14] 和 Webmon [16] 是更依赖应用程序级注解的系统示例,而 XTrace [12]、Pinpoint [9] 和 Magpie [3] 主要关注库和中间件的修改。 Dapper 与后一组关系最为密切。与 Pinpoint、XTrace 和 Magpie 的早期版本一样,Dapper 使用全局标识符将来自分布式系统各个部分的相关事件联系在一起。与这些系统一样,Dapper 试图通过将工具隐藏在通用软件模块中来消除对应用程序进行注解的需要。 Magpie 通过采用为每个应用程序编写并明确描述事件之间关系的事件模式,规避了全局 ID 的使用以及正确传播它们的挑战。我们不清楚模式在实践中实现透明度的效果如何。 X-Trace 的核心注解要求比 Dapper 的要更加雄心勃勃,因为不仅在节点边界处收集追踪,而且在节点内的不同软件层之间传递控制时也会收集追踪。我们对低开销检测工具的严格要求使我们放弃了这样的模型,而转向创建最小的机制,使代表给定原始请求完成的所有工作能够捆绑在一起。这仍然可以通过可选的应用程序注解来丰富 Dapper 追踪。

9 结论

在本文中,我们介绍了 Google 的生产环境分布式系统追踪平台 Dapper,并报告了我们开发和使用它的经验。 Dapper 部署在 Google 几乎所有的系统中,并且允许追踪我们绝大多数最大的工作负载,而无需进行任何应用程序级别的修改,并且没有明显的性能影响。 追踪用户界面的流行证明了 Dapper 对开发人员和运营团队的实用性,并在此处通过用户场景进行了说明,甚至有些用户场景是其设计者没有预料到的。

据我们所知,这是第一篇讲述大型生产分布式系统追踪框架的文章。 事实上,我们的主要贡献源于我们对一个运行了两年多的系统进行了回顾性报告。 例如,我们发现将最小的应用程序透明追踪功能与简单的 API 相结合以增强追踪功能的决定是值得的。

我们相信 Dapper 实现了比以前基于注解的分布式追踪系统更高程度的应用程序级别透明度,正如需要手动干预的少量工作负载所证明的那样。 虽然我们的计算部署有点不寻常的同质性促进了这一点,但这仍然是一个重大挑战。 最重要的是,我们的设计为实现应用程序级透明度提出了一些充分条件,我们希望这些条件可以帮助其他人为更多异构环境开发解决方案。

最后,通过向内部开发人员开放 Dapper 的追踪存储库,我们创建了比 Dapper 团队能够单独开发的分析工具更多的分析工具,极大地节省了设计和实现工作。

致谢

我们感谢 Mahesh Palekar、Cliff Biffle、Thomas Kotzmann、Kevin Gibbs、Yonatan Zunger、Michael Kleber 和 Toby Smith 提供的实验数据和关于 Dapper 体验的反馈。 我们还要感谢 Silvius Rus 在负载测试方面的帮助。 最重要的是,我们感谢多年来持续开发和改进 Dapper 的优秀工程师团队; 按出场顺序,Sharon Perl、Dick Sites、Rob von Behren、Tony DeWitt、Don Pazel、Ofer Zajicek、Anthony Zana、Hyang-Ah Kim、Joshua MacDonald、Dan Sturman、Glenn Willen、Alex Kehlenbeck、Brian McBarron、Michael Kleber、 Chris Povirk, Bradley White, Toby Smith, Todd Derr, Michael De Rosa, and Athicha Muthitacharoen。 他们都做了大量的工作,使 Dapper 成为谷歌的日常现实。

参考

  1. M. K. Aguilera, J. C. Mogul, J. L. Wiener, P. Reynolds,and A. Muthitacharoen. Performance Debugging for Distributed Systems of Black Boxes. In Proceedings of the19th ACM Symposium on Operating Systems Principles,December 2003.
  2. P. Bahl, R. Chandra, A. Greenberg, S. Kandula, D. A.Maltz, and M. Zhang. Towards Highly Reliable Enterprise Network Services Via Inference of Multi-level Dependencies. In Proceedings of SIGCOMM, 2007.
  3. P. Barham, R. Isaacs, R. Mortier, and D. Narayanan. Magpie: online modelling and performance-aware systems. InProceedings of USENIX HotOS IX, 2003.
  4. L. A. Barroso, J. Dean, and U. H¨olzle. Web Search fora Planet: The Google Cluster Architecture. IEEE Micro,23(2):22–28, March/April 2003.
  5. T. O. G. Blog. Developers, start your engines.http://googleblog.blogspot.com/2008/04/developersstart-your-engines.html, 2007.
  6. T. O. G. Blog. Universal search: Thebest answer is still the best answer.http://googleblog.blogspot.com/2007/05/universalsearch-best-answer-is-still.html, 2007.
  7. M. Burrows. The Chubby lock service for looselycoupled distributed systems. In Proceedings of the 7thUSENIX Symposium on Operating Systems Design andImplementation, pages 335 – 350, 2006.
  8. F. Chang, J. Dean, S. Ghemawat, W. C. Hsieh, D. A. Wallach, M. Burrows, T. Chandra, A. Fikes, and R. E. Gruber. Bigtable: A Distributed Storage System for Structured Data. In Proceedings of the 7th USENIX Symposium on Operating Systems Design and Implementation(OSDI’06), November 2006.
  9. M. Y. Chen, E. Kiciman, E. Fratkin, A. fox, andE. Brewer. Pinpoint: Problem Determination in Large,Dynamic Internet Services. In Proceedings of ACM International Conference on Dependable Systems and Networks, 2002.
  10. J. Dean and S. Ghemawat. MapReduce: Simplified DataProcessing on Large Clusters. In Proceedings of the 6thUSENIX Symposium on Operating Systems Design andImplementation (OSDI’04), pages 137 – 150, December2004.
  11. J. Dean, J. E. Hicks, C. A. Waldspurger, W. E. Weihl,and G. Chrysos. ProfileMe: Hardware Support forInstruction-Level Profiling on Out-of-Order Processors.In Proceedings of the IEEE/ACM International Symposium on Microarchitecture, 1997.
  12. R. Fonseca, G. Porter, R. H. Katz, S. Shenker, and I. Stoica. X-Trace: A Pervasive Network Tracing Framework.In Proceedings of USENIX NSDI, 2007.
  13. B. Lee and K. Bourrillion. The Guice Project Home Page.http://code.google.com/p/google-guice/, 2007.
  14. P. Reynolds, C. Killian, J. L. Wiener, J. C. Mogul, M. A.Shah, and A. Vahdat. Pip: Detecting the Unexpected inDistributed Systems. In Proceedings of USENIX NSDI,2006.
  15. P. Reynolds, J. L. Wiener, J. C. Mogul, M. K. Aguilera,and A. Vahdat. WAP5: Black Box Performance Debugging for Wide-Area Systems. In Proceedings of the 15thInternational World Wide Web Conference, 2006.
  16. P. K. G. T. Gschwind, K. Eshghi and K. Wurster. WebMon: A Performance Profiler for Web Transactions. InE-Commerce Workshop, 2002.