下载本文的 PDF 版本 PDF

来自生产线的经验教训

制造业可以教会我们很多关于大规模互联网服务性能衡量的知识。

DANIEL ROGERS,微软

一月份的月度服务质量会议像往常一样开始了——来自开发、运营、市场营销和产品管理的代表围坐在桌子旁,议程重点是上个月的绩效。和往常一样,影响客户的事件和服务质量是关键议题,我准备了数据,显示我所代表的服务部分(MSN,微软服务系列,包括电子邮件、即时通讯、新闻、天气和体育等)的平均正常运行时间。

运行如此大规模的服务,幕后有数千台服务器,使得将性能简化为简单的平均值变得困难。管理一个提供新闻、天气、股票报价、即时消息、博客和数亿电子邮件帐户的服务,通过汇总正常运行时间百分比已被证明效果不佳,因为许多细节隐藏在数字背后。我们当月处理的客户服务电话仅占所提供整体服务的 0.004% 略多(没错,是千分之四的百分之一)——所以这算不算问题呢?

更好的问题是,“什么的百分比?” 月度比较是不够的。我们需要深入研究这些数字,以更好地理解管理和衡量大规模互联网服务的确切含义。

性能衡量挑战

MSN 服务通常由三种服务器角色组成:前端 Web 服务器、中间层事务服务器和后端存储服务器。MSN 的不同组件可能使用所有或仅使用其中一些角色。在三层服务的情况下,Web 服务器使用各种协议与中间层服务器通信,具体取决于服务的需求。例如,Hotmail 使用面向电子邮件的消息协议,每条消息定义一个针对电子邮件存储的原子工作单元。负载均衡器处理用户流量的峰值。典型的网页刷新会导致来自 Web 服务器的多个顺序请求通过中间层服务器。一般来说,我们尽量在执行给定页面绘制的 Web 服务器和管理针对底层数据存储的事务的中间层服务器之间保持无会话关联。

在这种安排中,将可用性降低为代表服务质量的单个数字变得困难。生成平均性能的解决方案涉及创建测试通行证帐户,并使用自动化代理来模拟登录和执行服务组件的功能。构成较大 MSN 的每个组件(例如 MSN Messenger 或 Hotmail)都有专门的代理来衡量服务质量。因此,第一个数字问题是如何管理充分覆盖 MSN 服务的许多部分所需的庞大数量的测试帐户。由于代理需要捕获管理用户特定数据的每个可能存储上的问题,因此最佳实践是在每个分区上都有一个测试帐户。这加起来就是数千个测试帐户。要了解 MSN 服务的各个部分运行状况如何,意味着要扩展测试用户帐户的数量并增加模拟会话的数量,以覆盖服务的所有部分。

随着时间的推移,我们发现基于模拟会话和测试帐户的衡量基础设施本身就变成了一个可扩展性问题。为了快速了解为什么会这样,请考虑每个保存用户数据的分区(例如,用于保存用户的电子邮件、Messenger 好友列表、股票甚至颜色偏好的磁盘)意味着每个涉及的存储上都会增加数百个测试帐户。为什么这很困难?以下是一些原因

我们得出结论,会话模拟器对于在某些东西无法正常工作时进行故障排除很有价值,但随着服务规模的扩大,它们不是衡量服务可用性的最佳方式。确定所提供服务的总体质量需要在单元事务级别进行更深入的衡量。

借鉴制造业

制造业在复杂产品的质量抽样方面面临着与我们类似的挑战。在 Web 服务中,我们具有能够简单且廉价地模拟客户交互的优势(至少在小规模上是这样)。然而,当涉及到衡量具有多个方面的产品的质量时,尝试定义一个汇总指标来代表质量的方法是失败的。在 Hotmail 中,我们从大型制造运营中寻找灵感——并从中获得了几种有用的技术,这些技术更符合我们的业务目标。

考虑任何大规模复杂工程产品的制造,例如汽车、摩托车或小型家电。客户满意度——质量的衡量标准——是多方面的。制造商需要量化客户做出判断的方式。客户可能会说,“这个烤面包机真令人失望。” 导致这种失望的最重要方面是烤面包机外壳油漆颜色的均匀性吗?装配和表面处理(产品使用时的手感)是一个因素吗?还是早餐后早餐,以统一的质量制作多个面包片的能力是决定性因素? 重要的信息是,复杂的产品具有复杂的质量目标。对于像 Hotmail 这样的基于 Web 的电子邮件服务,我们需要方法来确定客户对许多服务质量衡量的优先顺序。

例如,在 Hotmail 中,装配和表面处理转化为外观和感觉。执行的一致性(烤面包)转化为登录、查找新电子邮件、删除任何不需要的消息、阅读消息和注销所需的时间。如果花费的时间太长,或者行为不一致,客户就会感到沮丧。如果客户的浏览器遇到不稳定的行为——例如,如果打开电子邮件有时很快,有时却很慢,或者如果某个操作明显比其他操作慢——客户对服务质量的感知就会一落千丈。如果客户有太多糟糕的体验,他们就会转向其他服务。MSN 市场营销研究表明,响应时间过长以及响应不一致是客户认为导致他们对整体 MSN 体验不满意的关键因素。

对于由多层组成的服务,例如基于三层架构的服务,衡量和实现一致性并非易事。例如,负责给定层的编程团队只能控制其代码的运行状况。然而,用户体验是所有层协同工作的结果。因此,对于管理提供对用户特定数据(例如电子邮件或好友列表)访问权限的服务器的团队而言,单个事务的计时以及该计时的一致性对于提供可预测的用户体验至关重要。运行 Web 服务器的团队传统上承担着管理用户体验的任务,但调用堆栈中较低的层以及负载均衡器和网络延迟等组件都会产生影响。因此,要衡量给定层对总体服务水平的影响,需要在所有层之间划分衡量标准。

统计服务管理

让我们以 MSN 的一项大型服务为例。在 Hotmail 中,收集统计测量值的第一个方法是识别影响客户体验的事务。我们在初始阶段识别了 32 种重要的事务类型。我们希望区分在事务级别区分良好体验和不良体验的衡量标准,并避免在衡量中引入不确定性。经过一番讨论,我们意识到好与坏之间的区别是渐进的,而不是二元的“是/否”方式。鉴于系统的复杂性,我们预计某种程度的随机性(不同的帐户、不同的存储、任何时间点的不同流量级别)将成为正常衡量的一部分。

通过研究制造业中使用的称为 SPC(统计过程控制)的技术,我们了解到好与坏的分布将是正态的(钟形曲线)。有些结果会非常好,其他结果(希望很少)会超出容差范围或不好。然而,大多数将落在正常范围内。为了获得所需的粒度级别,我们开始研究如何捕获测量值。由于我们关注的主要用户体验指标是计时的一致性,因此我们从事务时间的测量开始——并遇到了我们的第一个挑战:处理大量的计时数据。

对于像 Hotmail 这样的服务,每天很容易超过 10 亿次用户驱动的事务,为每个事务测量计时数据很快就会增加到一个非常大的数据管理问题。我们在使用模拟事务时面临的挑战再次迅速变得明显。主要问题是

最终,我们决定最好的解决方案是测量每个事务,但为此,我们必须以直接有意义的形式存储数据。这意味着使数据可用于收集。

在查看 Windows NT 中的性能计数器(这是一个很好的首选)之后,我们意识到它们不适合公开分级测量。为了表示从非常好到非常糟糕的五个等级,我们需要为我们想要公开的每个测量值创建五个计数器。这增加了许多复杂性。此外,我们使用自定义性能计数器的经验表明,每次系统重新启动时都需要重新创建它们。使用性能计数器公开此数据意味着服务需要启动例程。对于基于 NT 服务(Windows NT 中 Unix 守护程序的等效项)的实现,服务前启动例程增加了复杂性。这种增加的复杂性使它们变得脆弱且容易出错,无法公开测量值:如果创建性能计数器的启动脚本中出现问题,则无法测量服务,并且实际上可能会意外停止,因为其所需的性能计数器不存在。这两个因素使 NT 性能计数器成为一个糟糕的选择。我们最终使用的解决方案是我们称之为性能曲线的东西。

性能曲线是接受以下事实的结果:对于任何给定的事务类型,在任何给定的时间段内,行为的分布都是可预测的。在大多数情况下,这将是正态分布。某些缓存策略也会引入双峰分布(例如缓存搜索结果——缓存命中将显示为一个峰值,而缓存未命中将显示为测量中的第二个峰值)。由于任何复杂系统中固有的随机性,单个性能测量本身并不有趣。单独查看时,您无法判断单个测量是否代表问题。如果我们定义代表不同行为类别的数值范围——例如非常好、非常好、好、差和非常差——然后计算在固定时间段内落入每个范围的单个事务的数量,那么结果就是一个小数据样本,可以直接衡量事务类型的行为。

图 1 显示了一个性能曲线示例,该曲线表示一个名为 GetData 的事务类型在五分钟期间(称为测量间隔)的总事务计时。图 1 中的图形表示是根据软件捕获的数据绘制的。每个图实际上是一组测量值,用于计算在固定测量间隔内每个范围内完成的事务数量。每组这样的相关测量值代表一个虚拟图,该图是自洽的,并代表该事务类型在一段时间内的性能测量值。

单个数据点表示落入某个值范围内的计数——在本例中为响应时间。如图 1 中标记的那样,一组计数表示 OK 性能,而其他计数表示好、差等。为了体现这些,收集数据的软件需要知道与每个范围关联的计时边界。这些边界对于每种事务类型可能是唯一的,并且在我们的实现中,它们基于在加载测量库时加载的配置文件中的设置。与我们基于测试帐户的抽样相比,这种在时间间隔内收集计数,然后制定代表不同性能类别的不同范围的组合,可以大大压缩数据。

通过捕获一行数据,其中包含事务类型、时间间隔和五个性能范围(存储桶),一行测量数据(五个长整型和一些键数据)代表了原本每个事务的测量值。因此,在图 1 所示的示例中,一个曲线测量值代表相当于 223,750 个单独的计时数据测量值——这是在五分钟间隔内对 GetData 事务的调用次数。这种数据压缩级别使得管理完整抽样策略(测量 100% 的事务)成为可能。在某些情况下,我们进一步分解数据,以便我们可以识别收集数据的服务器实例。对于每天处理超过 10 亿个 GetData 事务的服务组件,数据压缩在高峰时段接近 85,000 比 1——这是一个巨大的压缩。

当将管理 MSN 服务所需的样本行数与来自我们传统会话模拟器的数据量(每个逻辑存储分区一个测试帐户,会话模拟器的传统回放间隔为 15 秒)进行比较时,我们发现使用性能曲线管理的数据量更少。当我们考虑到不需要管理测试帐户并在添加更多存储时添加更多模拟器时,决定非常简单:性能曲线以更低的成本提供了更好的数据和更好的粒度。

速度——使数据可用

在决定测量每个事务时要考虑的一个方面是与测量相关的开销。为了可以接受,将数据驱动到集中式存储的成本不得对正在测量的应用程序的性能产生重大影响。在 MSN,我们决定不应存在与测量存储相关的阻塞行为。这影响了我们如何在仪表化的应用程序组件中捕获曲线测量数据;如果在曲线数据基础设施中发生任何故障或延迟,我们宁愿丢失测量值,也不愿影响调用方。

在我们的 1.x 生产实现中,我们决定每种收集服务器类型都会缓存几个间隔的数据,然后后台线程会定期将数据写入收集存储。这种方法将测量值的存储与测量值的收集分离开来。然后,存储线程使用单向 SOAP 发出单个非阻塞 Web 服务调用,完成之前做出的“不等待”的决定。

图 2 显示了逻辑收集管道。单个测量值被分桶(即,放入适当的桶中)。在测量间隔结束时,与数据点关联的值被刷新到测量缓存,然后重置。单独的线程定期通过单向 SOAP 调用将所有收集的测量值刷新到存储,并清空缓存中已发送到存储的所有测量值。这是在刷新间隔边界上完成的,刷新间隔边界也是可以配置的时间段。

通过配置测量间隔和刷新间隔,我们可以控制总体上生成多少行数据(每个收集服务器每种事务类型一行),以及我们调用 Web 服务的频率。这种可配置的组合使我们能够几乎无限地扩展解决方案,但代价是粒度。

使配置可管理

在原型化该方法后,我们注意到,由于每个测量桶都代表一个值范围,因此可能会错误地将间隙或重叠配置到配置文件中。在理想情况下,每个桶代表一个离散的数字范围,当您查看所有桶时,每个可能的值都以连续的方式落入一个且仅一个桶中。

为了简化配置并使值间隙不可能发生,我们使用了我们称之为“鸡蛋分拣算法”的实现。这种术语可以追溯到用不同大小孔的溜槽滚动鸡蛋来分拣鸡蛋的时代,这些孔从小到大排列。每个鸡蛋要么沿途掉入一个孔中(从而按尺寸范围分拣),要么因为它比任何孔都大而滑落末端。

在软件中,给定五个桶,我们需要四个值来表示一组连续的范围。每个数字代表桶的上边界,使得任何小于边界值的值都将落入具有该边界的桶中。经过四次检查后,第五个桶会自动递增。这种方法不仅通过最大限度地减少比较次数来简化编码,而且还通过不必担心桶配置中的间隙和重叠来简化管理大量事务的边界配置。

使用方差进行决策

实施这种测量系统的目的是提高系统工作方式的准确性和可见性。这种复杂程度的测量系统使我们能够根据数据做出决策。当我们朝这个方向前进时,首要问题是如何使用这种数据。

将数据排列在离散集中的有趣之处在于,任何给定的数据行本身都代表完整的信息。与早期技术(例如通过生成测试事务进行抽样的自动化会话模拟器)相比,基于处理的每个事务收集曲线数据的技术产生了更具可操作性和真实性的数据。我们曾经有会话模拟器(测量抽样事务的平均值)和之前的性能计数器(测量与时间无关的单个值),现在我们有了比较数据,这些数据表明单个规模单元组件相对于自身的行为。这种自包含的特性有助于确定给定服务器场在某个时间点、在较小的时间单位内甚至在更长的时间段内表现出的行为组合。

在一个实现中,我们立即注意到按消息大小分解的事务类型行为不相同。在我们的实现中,我们可以分别测量小型、中型和大型消息的计时。这使我们能够解决不同消息大小自然会产生的不同阈值(好到坏)。我们的想法是,带有 10MB 附件的消息应该比没有附件的 2KB 消息花费更长的时间才能返回给请求用户。这种想法促使我们添加逻辑,使正在管理的数据的大小成为分桶步骤中的一个因素。大型消息与小型消息分开跟踪,并且可以使用不同的配置阈值。逻辑被通用化,以便我们只需更改配置即可添加大小分解(无需重新编译!)。

凭借这些功能,我们现在能够将一天分析为每个测量的曲线/事务类型的时间序列。术语“事务”和术语“曲线”之间的区别在于相关程度,是否存在大小分解。可以通过分析数据中的变异程度来做出决策。如果一切运行良好,则方差应保持稳定。方差——一种统计测量值,是每条曲线分布的标准偏差的平方——可用于定位异常行为。一个行为良好的系统是可预测的,并且有望实现线性扩展。

假设同一规模单元上的事务在同一时间附近应以类似的方式运行。因此,当您开始查看不同的分布(一种事务类型的故障多于另一种)以及当您查看数据随时间的变化时,感兴趣的区域会突出显示出来。一个行为稳定的系统在负载下可能会变慢,但一个显示波动计时或显示按消息大小(按精心选择的边界归一化)显示不同方差的系统可能表明存在优化机会,甚至可以识别缓慢或有缺陷的代码。

控制中的测量:圆满结局

自从我们在几个 MSN 服务组件中实施性能曲线以来,月度 QoS(服务质量)会议不再是猜测平均值含义的游戏。当经理要求细分我们的 QoS 总体事务组件的数据时,我们可以详细显示细分情况。每个数据切片都是自洽的,因此您可以查看性能曲线数据中任何维度的相对贡献。无论是按规模单元、服务组件、事务还是消息大小,我们都可以显示衡量通过系统的每个事务执行情况的详细信息。因此,我们从客户角度对 QoS 的理解得到了提高。

此外,正如我们在前面能够识别与消息大小相关的意外行为的案例中强调的那样,增加的可见性使我们更好地了解与软件和硬件架构相关的决策的影响,并为我们提供基于数据做出优化决策的数据。

最后,从向服务组件添加检测的投资回报角度来看,实施可能需要几个月才能到位。计划要测量系统的哪些部分,获得开发团队和业务经理的认可,然后启动第一个实现并收集数据需要大量投资。使数据可见并提出您将用于确定服务质量级别的边界需要一段磨合期。关于是聚合曲线数据以进行报告还是将其全部加载到数据仓库的决定也将是特定于情况的——您做得越多,您的可见性就越好,但成本也越高。现在,我认为这种方法正在为 MSN 带来回报,但成本很高。

DANIEL ROGERS 是微软 Windows 部门的项目经理。他从事大规模分布式应用领域的工作已有 20 多年。他于七年前加入微软。

acmqueue

最初发表于 Queue 第 3 卷,第 10 期
数字图书馆 中评论本文





更多相关文章

David Collier-Brown - 你对应用程序性能一窍不通
当您遇到性能或容量规划问题时,无需进行全面基准测试。一个简单的测量将提供系统的瓶颈点:这个示例程序在每个 CPU 每秒处理八个请求后会明显变慢。这通常足以告诉您最重要的事情:您是否会失败。


Peter Ward, Paul Wankadia, Kavita Guliani - 在 Google 改造后端子集化
后端子集化对于降低成本非常有用,甚至对于在系统限制内运行可能是必要的。十多年来,Google 使用确定性子集化作为其默认后端子集化算法,但尽管该算法平衡了每个后端任务的连接数,但确定性子集化的连接流失率很高。我们在 Google 的目标是设计一种具有降低连接流失率的算法,该算法可以取代确定性子集化作为默认后端子集化算法。


Noor Mubeen - 工作负载频率缩放定律 - 推导和验证
本文介绍了与每个 DVFS 子系统级别的工作负载利用率缩放相关的方程式。建立了频率、利用率和缩放因子(本身随频率变化)之间的关系。这些方程式的验证结果证明是棘手的,因为工作负载固有的利用率也似乎在治理样本的粒度上以未指定的方式变化。因此,应用了一种称为直方图脊线追踪的新方法。当将 DVFS 视为构建块时,量化缩放影响至关重要。典型应用包括 DVFS 调控器和或影响系统利用率、功耗和性能的其他层。


Theo Schlossnagle - DevOps 世界中的监控
监控看起来可能非常复杂。最重要的是要记住,完美永远不应成为更好的敌人。DevOps 使组织内部能够进行高度迭代的改进。如果您没有监控,那就获取一些;获取任何东西。有些总比没有好,如果您已经接受了 DevOps,那么您已经注册了随着时间的推移使其变得更好。





© 保留所有权利。

© . All rights reserved.