分布式追踪系统,最佳核心设计实践

1 引言

现代分布式服务是巨大的、复杂的,日益依赖其它类似的复杂分布式服务。比如,许多谷歌服务使用各种内部服务,例如,广告服务和拼写检查服务;并且部署atop基础服务,例如BigTable,横跨数以百计的节点,构建诸如谷歌文件系统(GFS)和Chubby锁服务等其它atop服务。即使简单的网络应用也会涉及很多层级,其中一些层级是可扩展和分布式的。管理和开发任务,例如性能调试、容量计划、问题诊断,在这样的开发环境中,一直很困难的,甚至非常困难,传统的机器为中心的监控和追踪机制成效有限。特别是,它们不能提供一个连贯的工作视图,来反映分布式服务节点及其依赖关系。

为了解决此类问题,近期通过研究,开发了新的工具和方法,这套工作流为中心追踪技术,我们通称为“端对端追踪系统”[2,5, 9,11,19,20,23,25,37,38, 40, 43,44,45, 47, 48,53]。端对端追踪系统获取单个分布式系统组件内部、组件之间,因果相关活动的详细工作流程。比如,一个基于分布式服务请求,每个追踪会显示服务组件内部,组件之间处理某个请求的工作流程,详见图一。

如单一进程调试工具应用于单一进程应用,端对端追踪系统获得分布式服务请求处理详细信息,此信息对开发和管理是无价的。截止目前,端对端追踪系统被证明效用是非常高的,采用连续样本测试,显示此系统使用许多重要的用例,包括异常测试,稳定状态正确性以及性能问题诊断,性能分析,资源使用分配。正是如此,有了越来越多的工业级实现,包括谷歌Dapper,Cloudera的Htrace,推特的Zipkin,以及其它。展望未来,端对端追踪系统很有可能成为云环境基础底层组件,为数据中心内部,数据中心间提供一个全局视图。

0?wx_fmt=png

图一:端对端追踪

此追踪显示了单个分布式系统以及其它可能依赖的分布式服务选项,其组件内部、组件之间,因果活动的工作流程。单个踪迹可能包含工作流的结构,比如工作执行的因果顺序,并发数目,分支合并点,系统性能,资源使用情况。不过,即便是这样一个子集也需要收集不同的用例。在本例中,踪迹展示了结构和内部追踪节点延迟,两个请求提交至一个假定的应用服务器,并由一个共享表和一个共享分布式文件系统进行支持。顶层访问所需的数据存储在表的缓存中,而底层请求所需的数据必须并发地取自两个存储节点。追踪点作为简单的标记,显示工作流所经过的软件组件位置。

不幸的是,尽管对端对端追踪系统的兴趣日益强烈,但指导设计者构建这一新基础系统的资料确实寥寥。其中最大的问题是,现有的资料将端对端追踪系统看做是一种泛化“全能的”解决方案,可以处理许许多多可能的用例,比如稳定态问题诊断或资源属性。不过依据我们过去设计的两个最著名的追踪基础系统 (Stardust [40,47], and X-Trace [19,20]) 以及构建在此基础上的工具, Dapper [43]证明设计这样泛化的系统是没有根据的。比如,最初构建和使用的Spectroscope [40],作为一种端对端追踪工具,自动定位资源性能变化, 结果却令人异常沮丧。设计初衷,希望诊断工具能复用一个追踪基础系统,该基础系统早期设计用于资源属性任务,但这种“全能”的理念在应对高负载时局限明显。经历初次失败后,我们才意识到针对资源属性任务的追踪系统设计,不应展示需要回收的关键路径,甚至同步点,一些不谦容降低开销的抽样,甚至限制了诊断的使用性。依据这些经验修订了原版的追踪基础系统,这些都有助于本文诸多观点的阐述。

追踪系统的基本概念是明确的,分布式服务代码的某些部分,在执行时工具产生的数据,接着这些针对某个请求而来自不同代码部分的数据,被合并产生一个全局的跟踪。不过,以我们的经验,从四个重要的设计维度说明不同用例的追踪结果使用性:什么样的因果联系应该被保留下来,如何对此因果联系进行追踪,如何减少抽样开销,如何进行可视化端对端追踪。没有这些知识维度和权衡取舍,设计追踪基础系统最后旺旺事与愿违。当然物极必反,精细的观察很难支持所有可能的用例。实际上,以前从未就这些维度加以甄别,很好的理解消化,以至于很多追踪系统实现未能很好的满足它们的潜在需求。

本文作为追踪基础系统的设计者指南,依据我们的经验和过去十几年的端对端追踪系统研究,总结了端对端追踪系统核心设计维度,并介绍了每个选项的利弊。除了上述描述的维度,我们建议针对每个核心追踪用例,采用特定的设计点,并判断现有的追踪系统实现那些满足此条件,或者不满足那些条件。我们认为这是首篇介绍,如何甄别这些设计维度,如何取舍,这些如何深入影响一个追踪基础系统的可用性。

本文采用如下的组织方式。第二部分讨论端对端追踪系统用例和基础剖析。第三至六部分描述设计维度和它们的开销。第七部分应用这些知识,针对某些用例,选择何种设计,以及分析现有的端对端追踪基础系统对某些用例的适用性。第八部分端对端追踪系统潜在的挑战和机遇。

2 背景

本小节阐述了端对端追踪系统相关的背景,2.1小节描述了其核心用例,小节2.2列出了三种常见的端对端追踪系统常用方法,小节2.3描述了本文提倡的架构方式。

2.1 用例

表一总结了端对端追踪系统的核心用例,列出了适用它们的追踪系统实现。注意,有些列出的基础系统原本构想的用例比本表中的更加宽泛。比如,我们原本构想,原始的Stardust适用于资源属性和诊断。类似的谷歌的Dapper被证明作用比原先想的少,因为它不能用于检测特定的异常。而这种“构思适用”和“现实适用”之间的错配,本文希望能最小化。0?wx_fmt=png

本表列出了端对端核心用例和适用于它们的追踪系统实现。某些实现适用于多个用例。新版的Stardust和X-Trace加‡以区分。几乎所有的追踪系统实现都能用来建模或者总结负载,即便这些模型基于不同需求设计。

异常诊断:诊断相关用例涉及问题的甄别和调试,此类罕见的工作流(位于99.9百分位)不同于其它工作流。此类问题和正确性或性能有关,正确性比如组件的时间过期或者失败, 性能比如一个执行很慢的函数或者过度地等待一个很慢的线程。或者工作流之间巨大的差异在于其结构,延迟,资源利用;对于结构,比如执行的因果顺序,并发量,分拆合并点。Magpie[5]可以甄别正确性以及性能相关异常,而Pinpoint[11]异常检测组件重点作用于正确性问题。

稳定性问题诊断:这是另一个诊断相关用例,涉及多数工作流的甄别和调试问题。此类问题占据一些重要指标50或者75百分位。此类问题的或许出现在工作流的结构、延迟、或者资源利用,总体上说与性能相关,比如,存储节点配置的改变,一组请求此类节点,结果加大了响应时间。Pip[37],Stardust‡,X-Trace两个版本,Dapper,以及Pinpoint都是很好的稳定问题诊断工具。

分布式性能分析:分布式性能分析的目的是诊断运行缓慢的组件或函数。一个函数执行的时间开销不同,或许是跟它的调用方式有关,图表分析器针对每个特定的调用栈,维护一个单独的二进制文件,因此无需保存全部的工作流结构。Whodunit[9]基于此目的设计,可以用来绘制整个工作负载。Dapper和ETE可视化地展示单个工作流。

资源属性:设计本用例回答这个问题:谁应该为分布式系统组件底层栈中执行的一小块代码负责?这个涉及试图完成此工作的分布式系统某个组件,以及发起的某个客户端或者请求。Quanto和Stardust适合此类资源属性诊断。前者,在分布式嵌入系统中,将每个设备能耗使用同诸如感性或路由等高层次的活动联系起来,而后者,将每个组件资源使用,比如CPU时间或磁盘时间,同分布式存储系统或数据中的客户端联系在一起。注意基于资源属性的追踪系统特别适合记账和计费,特别是许多客户端共享分布式服务的情形,比如亚马逊的EC2云服务。

工作负载建模:这种泛泛的用例采用端对端追踪,创建工作负载概要,用于后续的分析或者推断。比如Magpie,聚集这些追踪结果来甄别独一无二的工作流集合,作为整个工作负载的典型代表。Stardust用来建立队列模型,回答“假如…会怎样?”问题。比如假如将一个分布式系统组件CPU替换成一个更快的,工作负载的性能会如何呢?几乎所有的追踪系统都能可以视为一个有用的工作负载模型,不过模型的具体类型由当初的设计决定。

2.2 端对端追踪方式

大多数端对端追踪基础系统采用以下三种方式的一种来甄别因果关系活动:元数据传播,规则,或者黑盒推测。本文追踪基础系统设计采用方式一,相对于其它两种方式,元数据传播更易扩展,追踪亦更加精确。当然,我们的许多分析同样也适用于其它方式。

元数据传播方式:和安全系统相似,端对端追踪设计为分布式系统的一部分,效果最佳。许多实现设计成白箱系统,这样组建可以被修改以产生元数据,比如ID,用来描绘因果关系活动。所有基于元数据传播的实现用来甄别功能点或者追踪点的执行因果联系,组合为日志消息,记录系统特定时间特定的点。为了运行时的开销降至最低,比如响应时间短,吞吐量小,因此,追踪系统可以“一直在线”,此范畴的许多追踪基础系统抽样仅收集小量的追踪点或者工作流。

基于规则的方式:少量的实现,比如ETE、Magpie,它们不传播元数据,但要求开发者写一个临时的联合规则,来确定定制化写入的日志消息中,各变量的因果关系。基于规则的方式不兼容抽样,只有所有日志都收集齐了,才能决定存在怎样的因果关系。因此,此方式扩展性不及元数据传播方式。

黑盒推测方式:几个端对端追踪实现[2,6,25,28,38,44,45,53]不涉及追踪系统的改造。而是依据关联的变量或者已存在的日志时序,或者简化假设,来推测因果关系。尽管无需修改软件就可以获得端对端追踪,听起来确实不错,不过此类方式不能恰当的抽提因果关联,特别是异步,比如缓存,事件驱动系统,并发,聚类,或者特定代码的设计模式,比如存储编码,所有这些在分布式系统中都司空见惯。

2.3 端对端追踪剖析

图二剖析了大多数基于元数据传播的端对端追踪基础系统。软件组件用来甄别分布式系统中那些已完成的工作,选择性的保留因果联系,降低开销,有选择地持久化存储这些追踪数据,创建踪迹,并将其呈现给开发者。整个过程包括追踪点,因果追踪机制,抽样机制,存储组件,追踪构造代码和展示层。

开发这样的基础系统需要回答两个关于设计理念的问题,来阐明基础系统基础功能。首先是,什么样的因果关系应该被保存下来?保存完整的开销会非常大,并且保存错误的会获得那些无用的追踪。针对前面小节甄别的用例,小节3描述了这些用例的那些因果关系应该被保留下来。

0?wx_fmt=png

第二个设计理念:什么样的模型应该用来展示这种关系?领域模型仅能表示少有的几种因果关系类型,存储和检索却会更加有效;而昂贵的模型则会作出相反的折中。最流行的最特别的模型,为有向无环树,有效地表达序列化、并发,或者递归调用/应答模式,比如在RPCs中的观察。分叉和并发由分支表示,采用原版X-Trace [20],Dapper [43],和Whodunit [9]。 Dapper还对调用/应答模式做了优化,即采用存储规则来方便检索。而Pinpoint[11]利用路径,有效地表示同步行为和事件驱动处理。

尽管很有用,树却不能用来表示拥有多个父节点的节点,在这些案例中,单个分布式系统事件,依赖好几个前面的事件。比如联结或处理依赖好几个输入。那保存这些联结对任务诊断就非常重要了,Stardust以及X-Trace修正版,采用通用的有向无环图替代有向无环树。原版Stardust同样采用有向无环图,建立原始工作提交者和聚类活动。Pip拥有DAGs一样强大的力量,通过任务中的任意消息来表述。

现在我们简单地阐述基础系统的基本组件,因果跟踪机制在工作流中传播元数据,保存我们想要的因果关系。这对端对端追踪系统来说最重要了,以及核心设计决策见小节4.

单个追踪节点显示单个工作流访问的代码库的位置。执行一个追踪点就会创建一个追踪点记录,所包含的信息与执行追踪点相关,相关的元数据,以及任何开发者想获取的附加数据,你如当前调用栈。而追踪点通常嵌入进通用的库中,比如RPC库,并由开发者加入分布式系统重要的部分中。尽管许多关于在哪个位置添加追踪点的设计决策,和日志系统相似,但追踪点还能甄别工作流结构,参看小节4.2,比如这样结构的追踪点可以用来追踪分叉和合并。或者,二进制重写或者插入技术,比如面向切面编程,亦可以自动将追踪点加入函数边界,或者诸如分叉以及合并的地方。

许多追踪基础系统采用抽样技术限制运行时以及存储开销。连贯的抽样常常被用到,即要么所有的,要么没有任何工作流追踪点被抽样。比如,采用连贯抽样来持久化,比如稳定的存储,所有工作流追踪点记录的百分之一都不到, Stardust [40]和Dapper [43] 为运行时开销百分之一不到。选择保存什么样的因果关系,决定了应该选择何种抽样技术。小节5描述了不同的抽样技术和利弊。

存储组件持久化了追踪点记录,而追踪构建代码合并追踪点记录,这些关联的元数据用来构建追踪因果关系活动。这些组件是可选的,因为追踪记录无需持久化,倘若能够在线分析。比如,对某些分析来说,这些足以传播因果关系活动的重要数据,在执行的追踪点读取这些数据。

有几个不错的工程选项,比如Dapper,可以最大化程度减少持久化追踪点记录带来的性能耗损。首先,在各控件处,抽样追踪点应异步进行记录,比如离开分布式系统的关键路径。可以将记录拷贝到一个,内存循环缓存中,或者在缓存满时将至丢弃,然而启用一个独立的线程将缓存中的追踪点,写入本地磁盘或者一个表中存储。最后采用MapReduce构建追踪。Stardus和Dapper建议存储两周的追踪数据进行事后分析。

端对端追踪基础系统最后一个环节是展示层,负责将构建的追踪数据展示给用户,这对诊断相关的任务来说很重要。各种追踪数据视图化处理、利弊权衡在小节6进行讨论。

3 理应保存什么样的因果关系呢?

存储因果关系活动是端对端追踪的终极目标,理想的追踪基础系统应存储所有真实的或必需的因果关系,甚至只保存这些。比如,保存工作流的单个服务请求及其背后的活动,读后写内存访问,缓存,文件,注册器,数据追踪,请求内部因果关系,比如资源竞争,组合状态等。

然而,了解活动之间真实的因果联系是困难的,正因如此,追踪基础系统借助Lamport的发生前(happens-before)关系(→),假定a和b为两个事件,a→b,那么a或许会影响到b,因此b或许因果依赖于a。不过发生前关系仅是一种近似的真实因果关系:不加甄别,且不完整。很难做到完整,是因为很难知晓所有的影响管道,有些管道在系统之外。而不加甄别,是因为捕获到了非因果关系,所带来的影响未必是真的。

追踪基础系统对这种不加甄别做出了限制,利用系统知识和环境,捕获仅为总发生前图的切面,这些部分很有可能包含必需的因果关系。首先,多数追踪基础系统对事件中的影响界限做出了假设。比如,假定一个内存保护模型,追踪基础系统或许会排除发生前边缘( happens-before edge),此类边缘介于不同进程,甚至基于不同单个线程的事件系统。(小节4机制中的伪假设边缘已被移除)

不同的切面用于不同的用例,但保存所有切面开销过大,即使最有效的软件漏洞追踪机制也会致使性能降低两到八倍。正因如此,追踪基础系统仅保存那些最有用的切面,来判断输出是怎么执行的。接下来的小节我们来阐述各切面适用于各种用例。

3.1 请求内切面

一旦开发一个追踪基础系统,开发者必须一个发生前图切面,定义这个分布式系统服务的工作流请求。发起请求的工作在执行前,应将客户端的请求响应作为工作流的一部分。然而,隐式的工作,比如遗留在写回缓存中的数据,最终必须写回到磁盘中,同样也应成为发起请求的工作流部分,或者作为请求的一部分,保证请求工作执行,比如命令缓存清空。通过观察形成了两个基本的请求间切面:发起者信息存储切面、触发器信息存储切面,它们持有不同的信息,应用于不同的用例中。首先,我们甄别了这些切面,它们之间的不同,试着理解为何原版的Stardust并不适用于诊断用。

小节3.1.1 和 小节3.1.2 描述涉及发起者信息存储切面、触发器信息存储切面种种利弊。小节3.1.3列出了存储这两个请求间切面的优势,而小节3.1.4讨论绘制并发行为的益处,这些并发行为来自顺序执行和存储于单个追踪的分叉合并。表格二显示端对端追踪系统的最有用的请求间切面。

3.1.1 发起者信息存储切面

保存本切面,意味着端对端追踪点,展示原始请求提交者和系统每个控件工作处理的因果关系。这对于资源属性非常有用,因为使用模型要求端对端追踪系统底层控件所做工作应与客户端、工作负载,或者原始请求的发起者相关联。Quanto [18],

3.1.2 触发器信息存储切面

图三中,发起者存储请求一的踪迹,视觉化处理后,并不那么直观,且难以理解,这是因为发送客户端响应之后,请求工作才算完成。同样,属于此请求的延迟工作在请求二的关键路径中得以执行(比如在回应之后,追踪点得到执行)。相反,触发器因果关系确保一个请求追踪显示,所有执行的工作,而后才可以发出客户端响应,包括另一个客户端的延迟工作,它在本次关键路径中得以执行。图三右边两个踪迹,显示了发起者存储例子中相同的请求,不过保存的是触发器因果关系。鉴于这些踪迹,视图化处理后,易于理解,最后以客户端响应结束,并且显示请求关键路径所有的工作,存储触发器因果关系用来做任务诊断,通常涉及“为何请求如此地慢?”问题的答案。

实际上,从存储发起者因果关系到存储触发器因果关系,或许这是我们对原版Stardust所做的最重要的改变,这样做特别适合任务诊断。许多其它追踪系统实现也隐式地保存了这个因果关系切面[11,19,37, 43]。

0?wx_fmt=png

0?wx_fmt=png

3.1.3 存储这两种切面是否可以获得一切呢?

以上推荐的切面尤为重要,应该存储起来用于各种用例,而不是单单存储其中的一种。事实上,同时存储发起者因果关系和触发器因果关系,有助于更深入地理解分布式系统,这可能是存储其中任何一个所无法比拟的。比如,诊断,存储发起者因果关系的同时,再加上触发器因果关系,追踪基础系统就可以回答诸如“谁应该负责废弃我的客户端缓存数据?” 或者 更通用的问题,“那些客户端可能会更多地干预其它客户端呢?”

 3.1.4 存储工作流结构(并发,fork,join)

对于发起者存储因果关系和触发器存储因果关系,存储工作流结构-并发行为,分叉和合并,为可选项。对于某些用例,这些是也没有必要,比如资源属性或者性能分析。而对诊断任务,这些却是很有用的。存储了并发和分叉,可以帮助开发者诊断并行过量或不足的问题。另外,存储合并可以帮助开发者诊断同步点过度地等待,且容易鉴定出关键路径。

原版X-Trace采用树形结构建模因果关系,因此无法存储合并。原版Stardust采用DAG,但并没有检测合并。为了更利于任务诊断,修正版的X-Trace采用DAGs,以及显式地包含检测合并API。

3.2请求间切面存储

除了请求内部的关联关系,请求之间或许存在很多类型的因果关系。本小节描述两种最常见的切面。

竞争性存储切面:请求之间资源争夺,比如共享变量的访问。诸多请求持有一个资源锁,并等待获取这个锁,存储这些因果关系有助于解释,意料之外的性能减速,或超时,仅有Whodunit存储本切面。

读后写存储切面:读取其它请求写入的数据,比如缓存或者文件,此读取请求或许会受到上下文的因果关系影响。举个例子,由某个文件上下文决定的工作,比如,一个map-reduce工作,其执行请求或许取决于文件原始的写者。存储读后写依赖关系有助于解释此类请求行为。

4 如何追踪因果关联关系?

所有端对端追踪基础系统必须探寻一种机制,即追踪请求内部以及请求间的因果关系切面,这些很可能与预期的用例有关。为了避免捕获肤浅的因果关联关系,比如一些并非我们所希望的切面,或错误的因果关联关系,追踪基础系统“线程”元数据伴随单个工作流,并采用相同或关联的元数据[9,11,19,20,37, 40, 43, 47, 48],建立项目间的发生前关联关系。小节4.1描述不同的元数据类型和它们之间的权衡利弊。

总之,元数据存储在线程局部变量中得以传播,当单个线程执行因果关联工作,并编程逻辑,传播元数据穿越常用的库边界,比如线程间,缓存间,或控件间。我们认为,系统的设计应该具备这样的功能,即通过执行流和消息,合理传播通用的元数据,因为这是所有追踪基础系统的核心。

尽管下面讨论的任何方式均可以通过建立发生前关联关系,来存储并发,还是需要额外的检测来捕获分叉以及合并。诸如结构性追踪点会在小节4.2中进行讨论。当然,追踪基础系统采用的因果关联关系模型,必须能充分地表示并发、分叉、合并。

4.1 各元数据类型的优劣

每种工作流元数据既可以是静态,也可以是动态的。动态元数据有额外的固定宽度或可变宽度。决定采用何种元数据时,有三个重要议题需要考虑。首先是大小,较大的元数据必然导致较大的消息,比如RPC,或者制约了有效载荷大小。其次是丢失、无法获取的数据脆度或者弹性。最后,该方法是否无需构建追踪,可实时获得全部的追踪信息,或者其它分析所需的数据。

比较这三种方式,相比可变宽度方式,固定宽度的方式限制元数据的大小。固定宽度方式对数据的获取性或丢失敏感,不一样的只是方式和程度的问题。动态、可变宽度的方式,对数据丢失有极大的弹性,不过以牺牲元数据的大小为代价。另外,动态、可变宽度的方式通常是避免构造追踪所必须的。表三总结了各种元数据传播方式的优劣。接下来,将会更详细地描述这些方式。

静态、固定宽度元数据:拥有此方式,单个元数据值,比如随机选择的工作流ID,用来鉴别所有因果关联活动。追踪系统实现采用此方法,需要显式地构建踪迹,采用相同元数据,对不同追踪点的日记进行合并。一旦开始做,就必须依赖存储于追踪点日志中的线索,建立发生前关联关系。比如,为了使单线程中的因果关联活动变得有序,必须依赖外部的时钟。网络消息必须经客户端发出后,才会被服务器端接收,正因如此,追踪基础系统依赖同步时钟,或许就可以建立客户端和服务器端工作的发生前因果关联关系,即使用网络对这两台机器上的追踪点进行收发。而为了鉴定控件内部的并发工作,追踪实现采用此方式,通过线程ID,建立发生前因果关联关系。Pip [37], Pinpoint [11], 和Quanto [18]采用静态、固定宽度的元数据。

该方法很是脆弱,可能会不恰当地序列化,丢失外部线索,比如,追踪点日志丢失;或者无法获取,比如开发者无法修改分布式系统代码库的任意区域。举个例子,线程ID丢失或者不可用,导致无法恰当地鉴定控件内的并发活动。

动态、固定宽度元数据:用本方法,除了工作流ID,简单逻辑时钟,比如单个64位的值,用来嵌入到元数据中,帮助追踪基础系统编码发生前关联关系,无需依赖外部线索。单个逻辑时间戳用来限制元数据大小。而矢量时钟(Vector clocks)不适用于固定宽度的元数据,因为矢量时钟要求元数据的宽度和整个分布式系统线程数目一致。在每个追踪点中,一个新的随机逻辑时钟值会被选中,而后将新、旧逻辑时钟存入相应的追踪日志中,来建立一个发生前关联关系。每个追踪点的自增计数器,同样被用来实现逻辑时钟,但作为序列化的并发访问显然是不够的。两个版本的X-Trace [19,20]采用动态、固定宽度元数据。而Dapper [43]以及两个版本的Stardust [40, 47]采用混合的方式,即动、静态,固定宽度元数据方式。例如,Stardust [40, 47]依赖外部时钟有序化控件内部活动,同时采用逻辑时钟有序化组件间访问。

同样,动态、固定宽度方式是脆弱的,一旦追踪点日志的某部分丢失,就很难有序化这些日志。比如,某个追踪点日志丢失,本方法便无法序列化这两个追踪片段,它们拥有完全不同的逻辑时钟值,因此不存在显式的发生前因果关联关系。而混合式方法,很少频繁地改变元数据的值,因此相较于那些追踪点元数据不停变化的方式,收到的影响小很多。还有一种减低脆性,却耗空间的方式。

0?wx_fmt=png

动态、可变宽度元数据:拥有此方法,赋值给因果关联关系活动的元数据大小是可变的。这样做,元数据包含线段树时钟,而非简单的逻辑时钟。和矢量时钟相似,线段树时钟减低了脆性,任何两个时间戳之间的比较,可以确定它们是并发,还是一前一后。与矢量时钟不同的是,线段树时钟按线程数目成比例增加或减少。而可变宽度的矢量时钟无法缩减,因此它的宽度需和工作流中最大的线程数目成比例。同时,矢量时钟需要一个全局独一无二,广为人知的线程ID。截止目前,没有一个追踪基础系统采用矢量时钟或线段树时钟。

倘若追踪基础系统希望迅速地获取全部的踪迹,或者其它需要将因果关联活动聚集在一起的数据,而且无需显式地构建踪迹,这就必须使用动态、可变宽度的元数据。 比如,采用动态、可变宽度的元数据追踪基础系统,会携带元数据中已执行的追踪点日记,一旦工作流结束,日记就可以拿来使用。Whodunit [9]是目前唯一一种元数据中携带追踪点日记(比如函数名)的追踪系统实现。为了减少元数据的规模,启发式编程用来减少传播追踪点日记数量,不过这样踪迹真实性也打折扣。

4.2 如何存储分叉(fork)、合并(join)?

对于上面讨论的静态、动态,固定宽度的元数据传播方式,分叉以及合并可以通过一对多和多对一追踪点进行保存。对静态方式,此追踪点必须包含这样的线索,能够鉴别出特定的分叉活动,或是等待活动,比如线程ID。对动态、固定宽度的方法,一对多追踪点应该包含当前逻辑时钟值,并且初始化的逻辑时钟值为后来的每个分叉后代所用。而合并追踪点需包含当前逻辑时钟值,以及所有事件的逻辑时钟值,这些必须在工作处理之前就做好。动态、可变宽度方法,倘若包含线段树时钟,便可推测分叉以及合并。

Mann等人采用另一种方式,即比较海量踪迹,自动地确定分叉点、以及合并点。

5 何种抽样才能降低开销?

抽样决定追踪基础系统持久化那些追踪点日记。端对端追踪基础系统最核心的技术问题就是减小运行时以及存储开销 [9,

这里有三个不同的基本选项,来决定抽取什么样的追踪点:基于头一致性的抽样,基于尾的一致性抽样,或者整体抽样。一致性抽样方法,确保工作流执行的所有全部追踪点,要么全部被抽样,要么都没有;在踪迹构建时,必须使用此方法,此踪迹用来展示因果关联活动。另外,基于头的抽样倘若用来存储发起者因果关系,会导致高昂的开销。图四展示了不同抽样规则之间的利弊,用它们存储不同的因果关系切面。接下来我们会进一步描述抽样规则。

基于头一致性的抽样方式:使用此方式,在整个工作流的起始,比如请求进入系统时,创建一个随机抽样决策;元数据沿着工作流传播,表明是否在收集追踪点。工作流随机抽样百分比由设定的工作流抽样百分比决定。和存储触发器因果关联的追踪基础系统结合后,工作流抽象百分比和追踪点抽样百分比持平。因为简单,基于头一致性抽样方法应用于很多现有的追踪基础系统 [19, 40, 43]。

0?wx_fmt=png

基于头一致性抽样方式,在追踪基础系统存储发起者因果关系时,并不能降低运行时、存储开销。这是因为有效的追踪点抽样百分比,多数时候高出工作流抽样百分比很多。为了理解这一点,不妨回想一下存储发起者因果关系,延迟工作属于原始的发起者。因此,一旦延迟工作被另外一个请求,或者后台活动聚集,聚集器执行的追踪点必须抽样,这样抽样的工作流得以将任意一个聚集集插入系统中。在许多系统中,此进程几乎会抽样到底层系统所有的追踪点。比如,倘若基于头的抽样方法,仅对0.1%的工作流,进行追踪点抽样。聚集前单个追踪点抽样概率也是0.1%。然而聚集32个项目后,概率上升至3.2%;经过过两个这样级别的聚集后,追踪点抽样百分比上升至65%。图四左边图样展示了单个级别聚集的膨胀过程。所有追踪点有效抽样百分比取决于几个参数:工作流抽样百分比、聚集级别数目,聚集级别之间的追踪点数目。

开发修正版Stardust [40]时,我们了解到基于头一致性的抽样方法和发起者因果关系不兼容。基于头的抽样方法作为第一个特征放入原版Stardust,早期Stardust没有抽样,也不存储发起者因果关系。那时,关于因果关系切面,或者这些切面如何与不同的抽样技术交互,我们都一无所知。因此,当我们将可抽样的Stardust应用于我们的分布式测试系统Ursa Minor [1]时,令人困惑的是追踪的开销没有降下去。当然,最重要的原因是,Ursa Minor包含一个缓存,此缓存靠近系统入口,一次聚集32项目。假如抽样的比例为10%,意味着聚集后,已执行追踪点有97%被抽样。

基于尾的一致性抽样方法:本方法和前者相似,只是工作流抽样决策发生在工作流的末尾,而非伊始。延迟抽样决策可以得到更智能的样本-举个例子,追踪基础系统可以检测工作流属性,比如响应时间,并且只收集异常的那些属性。生成抽样决策之前,每个工作流的追踪点日志必须缓存起来。并发执行的工作流有很多,每个请求都能执行很多追踪点,携带有延迟工作的工作流依旧会在系统中停留很长一段时间,不过这些收集的临时数据并不是一直有效的。

基于尾的抽样方法避免了追踪点抽样百分比过度膨胀,因为它无需提前提交抽样决策。正是如此,可以用来存储发起者因果关系活动,并且保持较低的运行时、存储开销。对于携带聚集工作的工作流,基于尾的抽样方法,确保携带有已聚集工作的工作流,所执行的所有追踪点要么全部被抽样,要么完全没有被抽样。要完成这个,需要维护一个映射,聚集器的工作流ID以及已聚集工作的工作流ID。图四最左边第二个图样,存储发起者因果关联活动时,采用基于尾抽样方法,追踪点日记必须缓存起来。因此对内存的要求比较大,所有很多追踪基础系统并不会用它。

而一些追踪基础系统采用组合规则,这些系统名义上采用了基于头一致性抽样方法,其实它们也在每个节点的循环缓冲中存入最近执行过的追踪点日记。循环缓冲常被调整用来确保,一个请求追踪点日记不会被废弃,只要其执行的时间没有超过50或者75百分位。此技术可以帮助追踪基础系统追踪收集,马上出现异常的然而并没有抽样的工作流,比如失败或者开始执行不久后出现的错误。尽管如此,对于某些出现的异常情形依然是不够充分的,比如执行时间很长的请求。

整体抽样方法:采用本方法,开发者可以直接设置追踪点抽样百分比,在单个追踪点设置抽样决策。不存在连贯性,因此通过此方法,踪迹是没法构建的。本方法最适合诸如资源属性这样的用例,需要分析的信息跟着工作流进程传播,直接从单个追踪点获取。

除了决定如何抽样追踪点,开发者必须确定对其中的多少追踪点进行抽样。许多基础系统选择随机地抽取一小部分,设置追踪点或者工作流的百分比-通常介于0.01%和10%之间。然而本方法只是捕获了少部分工作负载,其中很小的一部分追踪点,限制了它的使用。不过采用每个工作负载抽样百分比可以帮助我们,不过这需要预先知道工作负载的大小。一种更加灵活的方式,由Sigelman等人提出[43],作为一种自适应方式,追踪基础系统用来捕获一组追踪点或工作流执行速率,比如500追踪点每秒,或100工作流每秒,并且动态地调整追踪点或工作流抽样百分比完成这个设定的目标。尽管看起来不错,不过还是要小心避免偏差的结果,特别是捕获的数据用来进行统计时。建立在共享服务之上的分布式服务,自适应的抽样速率应取决于追踪开销,最底层的共享服务能支持,并等比例地传播到顶层服务。

6 如何对踪迹进行视图化处理?

良好的视图化处理对诸如诊断、性能分析这样的用例非常重要。有效的视图化处理会让开发者的工作事半功倍,而无效的则事倍功半,使得开发者不得不寻找其它的替代工具和技术[30,39]。确实如此,Oliner等人将视图化处理作为未来诊断研究的核心挑战之一[34]。本小节描述的是一种通用的视图化端对端踪迹方式。具体选择什么样的方式取决于视图化预先设定的用处,前期的设计选择,以及精确度是否高于要展示的数据量。还有,底层踪迹展示限制了视图化的使用。DAG支持本节中的任意方法,当然,几乎所有的流图亦可以构建自有向树。

表四总结了各种视图化处理的利弊。图五显示不同的视图化处理在请求展示方面的不同。而Pip采用非视图化踪迹的方式,即采用特定语言文本化地描述踪迹。正式的用户研究需要比较视图化处理和特定语言文本描述的相对优劣,这里我们不做相关分析。

甘特图又称甬道:此类视图常用来展示单个踪迹,同样,可以用来视图化具有相同工作流的多个请求。Y轴展示了整个请求,分布式系统发起的子请求;而X轴显示了相对的时间。Y轴上项目的相对起始时间和延迟(现实时间测量值)由横向的棒进行展示。并发很容易通过棒在X轴重叠的部分得以视图化的鉴别出来。同样,分叉以及合并也必须通过视图化的方式加以鉴别,不过难度要大一些。ETE [23]和Dapper [43]均采用甘特图来视图化单个踪迹。Dapper除了展示整个请求和子请求的延迟,还可以鉴别网络时间开销,即已观察的请求或子请求延迟减去服务器端的时间花销。

流向图又称请求流向图:这些有向无环图,在请求于分布式系统各组件中执行时,真实地反映了请求的工作流程。它们常被用来视图化,显示具有相同工作流的多个请求聚集信息。正因为此类请求通常拥有相似的执行流程,流向图被认为是一种非常好的保存精确度的方式,特别是发起多个请求时。图中的扇出(Fan-outs)表示并发活动(分叉)的开始,不同分支上的事件即并发,而扇入(Fan-in)表示同步点,即合并。在修正版的Stardust [40]和X-Trace [19]通过流向图对踪迹进行视图化处理。

调用图和焦点图:视图通常用来展示多个踪迹,但不展示并发,分叉或者合并,因此也就谈不上精确了。调用图采用扇出显示某个父函数对子函数的调用。焦点图则显示一个控件或函数,即所谓的“焦点节点”的调用栈,而调用图将焦点节点作为根节点。总的来说,焦点图特别适合诊断任务,开发者已知那些函数或控件有问题。Dapper用焦点图展示具有相同工作流的多个请求,不过由于面向RPC的自然属性,节点代表的不是控件或函数,而是客户端和服务端执行的所有RPC工作。注意,一旦采用不同的工作流来视图化多个请求,调用图对路径的展示将变得不可用[4]。图五为a → b → c → d路径的调用图。

0?wx_fmt=png0?wx_fmt=png

调用上下文树(CCTs)[4]:此视图特别适合不同工作流多个请求显示,确保每一个经过分布式系统,从根节点到叶节点的路径均为合法路径。为此采用一种很紧凑的方式,CCTs采用扇出的方式显示函数调用,而非分叉,正因如此,它的精度有限。CCTs可以通过分期常数时间构建,特别适合诸如高级别的系统行为总结等任务,比如性能分析。Whodunit [9]通过CCTs进行工作负载性能分析。

7 纵览全局

基于上文描述的利弊和我们的过往经验,本小节针对端对端追踪系统核心用途,而提出的良好设计选择。同样,我们也给出了前人的实现选择,并与我们建议的做比较。

7.1 推荐选项

表五斜字体行为端对端追踪系统核心用途提供的设计选择。对于因果追踪机制,针对多数用例,我们建议采用静态、动态组合,固定宽度方式,这就要求大小为常数,降低对外部线索的依赖,比起直接的动态、固定宽度方法弹性要好些。同样静态元数据也是一个不错的选择,你需要的外部线索,比如建立发生前关系,会一直存在;或很多事件需要的线索,比如分叉、合并、并发,无需存储。开发者需要考虑可变宽度方式是否可用,可否拿来使用。对于需要一致性抽样的用例,我们谨慎地推荐采用基于头的版本,如果它能满足需要的话;而基于尾的一致性抽样方法,同样也值得考虑,除了具备前者所具备的之外,用途更加宽泛。接下来介绍什么样的用例应采用怎样的设计。

0?wx_fmt=png表五:针对不同的用例,本文所推荐的设计选项,以及现有追踪系统所采用的设计选项。推荐的选项用斜体字表示,而已存在的实现设计选项,按照推荐选项的类似的方式排序。为了便于比较,基于规则的方式,比如Magpie and ETE同样,也包含在内。Stardust和X-Trace的修订版已Stardust‡、X-Trace‡形式进行表示。静态的元数据传播方式记为S,动态、固定宽度的方式记为DF,组合、固定宽度的方式记为S/DF,动态、可变宽度的方式记为DV。而V表示已陈述的项目某种变体。

异常检测:本用用于对罕见工作流的鉴别,与其它工作流差异很大,因此便于开发者分析。正因如此,应采用基于尾的一致性抽样方法,一是便于踪迹构建,二是追踪基础系统在决定是否抽样前,评估工作流是否异常。不论是触发器因果关系,或是发起者因果关系均可以以极低的开销得以保存。不过前者更受欢迎,工作流在视图化后,触发器因果关系可以展示关键路径,更容易被理解。为了鉴别来自并行过度、并行不足,并发过度等待异常,系统实现应存储分叉、合并、以及并发行为。流向图最适合对异常进行视图化处理,精确度高,而且异常检测不会产生太多结果。同样,甘特图也不错。

稳定性问题诊断:用例涉及性能问题、正确性问题诊断,这些问题常见于诸多请求中。它的设计选项和异常检测,不同的是,需要使用基于头的抽样方法,因为即便是低速率的抽样,问题依然不难被察觉。

分布式性能分析:本用例涉及抽样函数,追踪点间延迟。控件间、控件内函数栈调用必须存储起来,以便抽样项目能够基于上下文得以聚集,不过完整的踪迹无效构建。调用栈被紧凑地表示,这就要求抽样方法,动态、可变宽度的元数据传播方法必须有效地整合。整合后,这些选项有助于追踪点日记以元数据的形式存在,性能数据在线收集。如果需要考虑元数据大小,可以采用固定宽度元数据,结合基于头,或者基于尾抽样的方式,不过在线性能分析就变得不可能。栈的调用无需存储分叉、合并、或者并发。CCTs特别适合对分布式性能进行视图化处理,可以显示整个工作负载,并且不会出现错误路径。

资源属性:本用例涉及系统任意级别工作归属原始发起者问题,因此发起者因果关系必须保存。资源属性最好的服务方式为动态、可变宽度的元数据传播方式,以及整体抽样方法。这样组合有助于聚集项目的客户端ID以元数据的形式存在,因此可以实时、在线分析,无需构建踪迹。倘若需要考虑元数据大小,可以采用基于尾抽样和固定宽度元数据加以替换,不过在线、实时分析则难以实现。当然基于头抽样也可以用,不过带来的开销很大,在低级别的聚集后,便会抽取几乎所有的追踪点。尽管分叉、合并、并发并不需要存储,DAG必须作为底层数据模型被使用,用来存储原始发起者和聚集工作的关联关系。最后,本用例无需视图处理。

工作负载建模:本用例的设计决策取决于什么样的工作负载属性需要建模。比如,Magpie [5]建模工作负载,主要鉴别一组资源使用相关的流,资源使用代表了整个工作负载。正因如此,Magpie存储分叉、合并、以及并发行为非常有用。倘若要对本用例视图化,可以采用流向图,或CCTs,它们一次可以视图化多个踪迹。

7.2 现有系统实现选项

表五同样列出了现有追踪系统实现符合本文所倡导的设计维度。追踪系统实现多数可以依据用例进行分组,当然一个追踪系统实现或许适用于多个用例。对于一个既定的用例,追踪系统实现按照推荐设计选项相似的方式排序。这样的排序表明,适合某个特定用例的追踪系统实现,亦倾向作出我们所推荐的相似设计决策。接下来描述我们所推荐的核心案例与追踪系统实现选项的不同所在。

对于异常检测,我们建议使用基于尾的抽样方法,不过Magpie [5]和Pinpoint [11]没有使用任何抽样技术。收集和存储每个请求的追踪点,确保这两种系统实现不会丢失任何稀有事件,即异常;不过这样它们则无法扩展处理大规模工作负载。Magpie无法抽样,是因为不能传播元数据。而Pinpoint主要关注正确性异常,因此没有必要存储并发、分叉、合并。

对于稳定性诊断问题,我们建议对分叉、合并、以及数据结构进行显式地存储,不过Dapper没有存储合并,因为它采用树作为模型,表达因果关联关系,而非有向无环图。Mann等人[31]近期的工作,是对大规模Dapper踪迹进行比较,集中关注习得合并点(learning join-point)位置。接着,Dapper踪迹被格式化以便展示习得的合并点。同样Pip [37]不同于其它追踪系统实现的地方,就在于使用了特定的文本表达语言描述踪迹。Pip语言描述其它控件与某个开发者关切的控件之间如何进行交互,功能与焦点图相似。特定文本描述语言和焦点图特别适用这样的场景,开发者脑海里已在关注某个控件,而问题定位性任务则行不通。

修正版的Stardust [40]和X-Trace [19]使得原版在修改后更利于诊断任务。这两个修正版均独立地倾向于使用几乎所有相同的设计选项。

对于分布式性能分析,已有的追踪系统满足、甚至超越了我们的所建议的。然而对于资源属性,已有系统则没有使用抽样方法,亦不能扩展。

8 机遇和挑战

更多内容见:http://blog.jobbole.com/104456/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值