使用区分优先级的负载分流保持Netflix的可靠性

2020-11-03 00:01:50

当基础架构从系统故障中自我恢复时,观众如何能够在Netflix上观看他们最喜欢的节目。

对世界各地的司机来说,堵车是最令人沮丧的经历之一。每个人都慢得像爬行一样,有时是因为一个小问题,有时是毫无理由的。作为Netflix的工程师,我们不断地重新评估如何重新设计流量管理。如果我们知道每个旅行者的紧迫性,可以有选择地选择路线,而不是让每个人都等着,会怎么样?

在Netflix的工程设计中,我们的动力是确保Netflix在您需要的时候出现在您需要的地方。然而,就在去年,我们的系统还容易受到比喻意义上的交通拥堵的影响;我们有开/关断路器,但没有渐进的方法来卸载。出于改善会员生活的动机,我们引入了基于优先级的渐进式减负。

下面的动画显示了后端根据优先级限制流量时Netflix查看器体验的行为。当较低优先级的请求被限制时,播放体验保持不中断,并且观众能够享受他们的标题。让我们深入研究一下我们是如何做到这一点的。

发生故障的原因有很多:引发重试风暴的客户端行为不端、后端服务规模不足、部署不当、网络故障或云提供商的问题。所有这些故障都会给系统带来意想不到的负担,在过去的某个时候,这些例子中的每一个都会阻碍我们的成员发挥作用。考虑到这些事件,我们开始让Netflix更有弹性地实现以下目标:

我们设想的包含优先级节流和混乱测试的最终体系结构如下所示。

我们决定将重点放在三个维度上,以便对请求流量进行分类:吞吐量、功能和重要性。根据这些特征,流量分为以下几类:

Non_Critical:该流量不影响播放或会员体验。日志和后台请求就是此类流量的示例。这些请求通常是高吞吐量的,这在系统中造成了很大比例的负载。

Degraded_Experience:此流量影响会员体验,但不影响游戏能力。此存储桶中的流量用于以下功能:停止和暂停标记、播放器中的语言选择、观看历史记录等。

严重:此流量会影响播放能力。如果请求失败,成员在点击Play时将看到一条错误消息。

API网关服务(ZUUL)使用请求的属性将请求分类为Non_Critical、Degraded_Experience和Critical Bucket,并根据每个请求的个别特征计算1到100之间的优先级分数。计算作为第一步完成,以便在请求生命周期的其余部分可用。

大多数情况下,请求工作流在不考虑请求优先级的情况下正常运行。然而,与任何服务一样,有时我们会遇到这样的情况:我们的后端或Zuul本身都遇到了麻烦。当这种情况发生时,具有较高优先级的请求将得到优先处理。较高优先级的请求将得到服务,而较低优先级的请求则不会得到服务。该实现类似于具有动态优先级阈值的优先级队列。这允许ZUUL丢弃优先级低于当前阈值的请求。

在请求生命周期中,ZUUL可以在两个时刻应用减负:当它将请求路由到特定的后端服务时(服务节流),或者在初始请求处理时,这会影响所有后端服务(全局节流)。

ZUUL可以通过监控错误率和对后端服务的并发请求来感知该服务何时出现问题。这两个指标是故障和延迟的大致指示器。当超过这两个指标之一的阈值百分比时,我们将通过限制流量来降低服务的负载。

另一种情况是Zuul本身陷入困境。与上面的场景相反,全局限制将影响Zuul后面的所有后端服务,而不是单个后端服务。这种全球节流的影响可能会给成员带来更大的问题。用于触发全局限制的关键指标是CPU利用率、并发请求和连接计数。当超过这些指标的任何阈值时,Zuul将积极限制流量,以在系统恢复期间保持自身正常运行。这一功能非常关键:如果Zuul宕机,没有流量可以到达我们的后端服务,从而导致完全停机。

一旦我们确定了优先级,我们就可以将其与我们的减负机制结合起来,从而显著提高流可靠性。当我们处于糟糕的情况下(即超过上述任何阈值)时,我们会从最低优先级开始逐步丢弃流量。三次函数用于管理节流级别。如果情况变得非常非常糟糕,这一水平将触及曲线的尖端,扼杀一切。

上图是如何应用立方函数的示例。随着过载百分比的增加(即节流阈值和最大容量之间的范围),优先级阈值非常缓慢地落后于它:35%,仍在90%左右。如果系统继续降级,我们会在超过80%时达到优先级50,然后在95%时最终达到10,以此类推。

考虑到相对少量的请求会影响流可用性,限制低优先级流量可能会影响某些产品功能,但不会阻止成员按下“播放”并观看他们最喜欢的节目。通过添加渐进式的基于优先级的负载分流,ZUUL可以减少足够的流量来稳定服务,而不会引起成员的注意。

当Zuul决定丢弃流量时,它会向设备发送信号,让它们知道我们需要它们后退。它通过指示他们可以执行多少次重试以及他们可以在哪种时间窗口内执行重试来实现这一点。例如:

使用这种背压机制,我们可以比过去更快地停止重试风暴。我们根据请求的优先级自动调整这两个刻度盘。具有较高优先级的请求将比较低优先级的请求更积极地重试,这也增加了流的可用性。

要验证我们关于特定请求是否落入非关键、降级或关键桶的请求分类假设,我们需要一种方法来测试用户在该请求离开时的体验。为此,我们利用了我们的内部故障注入工具(FIT),并在ZUUL中创建了一个故障注入点,允许我们根据提供的优先级放弃任何请求。这使我们能够通过阻止特定设备或成员的优先级范围来手动模拟减负体验,让我们了解哪些请求可以安全地减负,而不会影响用户。

这里的目标之一是通过放弃预计不会影响用户流媒体体验的请求来减轻成员的痛苦。然而,Netflix变化很快,原本被认为不关键的请求可能会出人意料地变得关键。此外,Netflix拥有多种客户端设备、客户端版本和与系统交互的方式。为了确保在这些场景中限制非关键请求时不会给成员带来痛苦,我们利用了我们的基础设施实验平台CHAP。

此平台允许我们进行A/B实验,将少量生产用户分配给控制组或处理组45分钟,同时限制处理组的一系列优先级。这使我们能够捕获各种实时使用案例,并衡量它们对回放体验的影响。CHAP分析每个设备的成员KPI,以确定对照组和治疗组之间是否存在偏差。

在我们的第一个实验中,我们在Android和iOS设备中检测到低优先级请求的争用情况,这会导致零星的播放错误。因为我们实行连续实验,所以一旦初始实验运行并修复了错误,我们就安排它们定期运行。这使我们能够及早检测到回归,并保持用户流媒体。

2019年,在渐进式减负到位之前,Netflix流媒体服务经历了一次中断,导致相当大比例的会员在一段时间内无法玩游戏。2020年,也就是实施部署几天后,团队开始看到该解决方案的好处。Netflix经历了类似的问题,潜在影响与2019年的停电相同。与当时不同的是,Zuul的渐进式减负开始发挥作用,开始减负流量,直到服务处于健康状态,而根本不影响会员的游戏能力。

下图显示了稳定的每秒流式可用性指标流(SPS),而Zuul在事件期间根据请求优先级执行渐进式负载卸载。图表中的不同颜色表示被限制的优先级不同的请求。

当基础设施正在从系统故障中自我恢复时,成员们正愉快地在Netflix上观看他们最喜欢的节目。

在未来的工作中,该团队正在研究将请求优先级扩展到其他用例中,如设备和后端之间更好的重试策略、动态更改负载卸载阈值、以混沌测试为指导原则调整请求优先级,以及其他将使Netflix更具弹性的领域。

如果您有兴趣帮助Netflix在不断变化的系统和意外故障面前保持清醒,请联系我们。我们在招人!