你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

节流模式

限制应用程序实例、单个租户或整个服务可以使用的资源。 这样,系统就可以在突然或持续负载下运行并满足其服务级别目标(SLO)。

上下文和问题

云应用程序上的负载因活动用户及其活动而异。 更多用户在工作时间登录,系统在每个月底运行计算成本高昂的分析。 突然爆发也发生。 如果处理需求超过可用容量,则系统会减慢或失败。 当系统具有商定的服务级别时,该故障违反了 SLO。

根据应用程序的业务目标,多个策略处理不同的负载。 一种策略是 自动缩放,它将预配的资源与当前需求相匹配并控制成本。 但预配新资源需要时间并增加成本。 超出容量增长或预算的需求会产生资源赤字。

解决方案

自动缩放的一种替代方案是限制资源使用上限,并在使用量超过该上限时对请求进行限流。 当使用量超过阈值时,工作负荷会监视其自己的资源使用情况,并限制来自一个或多个用户的请求。 系统继续正常运行并满足其服务级别目标。

节流是一个控制循环,而不是单次准入决策。 系统需要三层的低延迟信号:基础结构利用率、应用程序状态和每主体计数器。 它持续测量饱和度,在定义完善的边界上强制实施限制,并在流量模式发生变化时调整这些限制。 重载是成熟的系统检测和恢复的正常操作模式。 限流可为您的工作负载提供 自我保护 能力。

系统可采用多种限流或相关策略:

  • 针对每个主体的速率限制: 拒绝在指定时间窗口内已超过配置速率限制的用户发出的请求。 此策略要求系统将每个请求归属于某个主体,并按该主体统计资源使用情况。 有关多租户工作负荷,请参阅 度量每个租户的消耗量。

  • 功能优雅降级: 关闭或降级非必要功能,以确保核心功能有足够的资源。 此策略以牺牲响应完整性来换取可用性。 例如,视频流式处理应用程序可以降低分辨率。

  • 负载平衡:通过使用队列来平滑活动量。 在多租户环境下,均衡会降低每个租户的性能。 当租户具有不同的服务级别协议(SLA)时,应立即处理高价值租户的工作,并暂缓处理低优先级的工作,直到积压减轻。 通过使用 优先级队列模式 或为每个优先级层公开单独的终结点来实现此方法。

  • 按优先级延后处理: 为低优先级应用程序或租户延后操作。 暂停或限制操作,并返回异常,提示租户稍后重试。

  • 出站速率限制: 当外部依赖项失败或返回错误时,限制自己的出站调用。 降低正在进行的请求计数以停止泛洪日志,并避免针对不正常的依赖项产生重试成本。 在依赖项恢复后还原正常请求流。 例如, NServiceBus 实现此功能。

下图显示了一段时间内使用三个功能(标记为 A、B 和 C)的应用程序的资源使用(内存、CPU、带宽和其他因素的组合)。功能是功能的特定区域,例如执行特定任务集的组件、执行复杂计算的代码片段或提供服务(如内存中缓存)的元素。

显示代表三位用户运行的应用程序的资源使用情况随时间变化的图表。

折线图以 y 轴表示资源利用率,以 x 轴表示时间。 三条彩色线条表示特征 A、特征 B 和特征 C,特征 A 的行最低,特征 B 的行位于中间,特征 C 的行最高。 图表顶部附近的实心水平线标记最大容量,其下方的虚线表示资源利用率的软限制。 两条垂直虚线标示时间 T1 和 T2。 在 T1 之前,所有三个特征线都会波动,特征 C 的线条上升并跨越软限制。 在 T1 时,功能 B 的曲线降至零,并一直保持为零,直到 T2,因为功能 B 被暂停,以便释放资源给功能 A 和功能 C。功能 C 的曲线在 T1 和 T2 之间回落到软限制以下,而功能 A 继续正常运行。 在 T2,功能 B 恢复运行,三条线均继续在软限制以下波动。

图表是堆积面积图。 功能 A 行下面的区域显示特征 A 消耗的资源、特征 A 和特征 B 行之间的区域显示功能 B 消耗的资源,以及功能 B 和功能 C 行之间的区域显示功能 C 使用的资源。 功能 C 的行位于堆栈的顶部,因此它还显示随时间推移的系统资源使用总量。

此图表显示正常功能降级。 就在 T1 之前,总资源使用接近阈值,并有可能耗尽可用容量。 功能 B 不如功能 A 或功能 C 重要,因此系统关闭功能 B 并释放其资源。 在 T1 到 T2 期间,功能 A 和功能 C 继续正常运行。 到 T2 时,总资源使用量会下降到足以重新打开功能 B。

可以结合自动缩放、优雅降级和限流,以保持应用程序的响应能力并满足 SLA 要求。 当预计需求将持续处于高位时,限流可在系统横向扩展期间保持稳定性。横向扩展完成后,系统将恢复全部功能。

下一个图表展示了总资源使用量随时间的变化,以及限流如何与自动缩放和其他补偿性控制措施协同作用。

展示节流与自动缩放结合效果的图表。

折线图以 Y 轴表示所有应用程序的资源利用率,以 X 轴表示时间。 两个水平引用行标记了资源利用率的软限制和自动缩放前的最大容量。 一条始于 T2 时刻的较高水平线,表示自动扩缩容后的最大容量。 利用率曲线随时间推移而上升并波动。 它跨越 T1 时的软限制,这是自动缩放开始的点。 在 T1 和 T2 之间,系统在自动缩放时受到限制,利用率保持在预缩放最大容量以下。 T2 时,自动缩放完成,限流放宽,利用率曲线跃升,并继续在新的、更高的最大容量下方波动。

在 T1 时,系统达到软限制并开始横向扩展。如果新资源未及时到达,需求可能会耗尽现有资源,并且系统可能会失败。 限流会在横向扩展期间拒绝超出部分的请求,以使资源使用量保持在硬性上限以下,然后在新增容量上线后解除这些限制。

Tip

边缘控制和限流模式解决的是不同的问题。 边缘控制(例如 Azure DDoS 防护 和 Web 应用程序防火墙 (WAF) 的速率限制规则)在网络边界运行,并在大流量攻击或恶意流量到达应用程序之前将其丢弃。 限流模式在应用程序内部运行,并根据应用程序定义的限制对 合法 流量进行度量和控制。 同时使用这两个层。 DDoS 防护不会阻止合法用户重载服务,应用程序限制不会吸收批量攻击。

问题和注意事项

在决定如何实现此模式时,请考虑以下几点:

  • 尽早作出限流决策。 限流是一项影响整个系统的架构决策。 后来对其进行改造是昂贵的。

  • 将节流限制调整为与最先达到饱和的组件一致。

    请求速率是限制的最熟悉维度,但实际瓶颈通常是并发正在进行的请求、队列深度、CPU 或内存利用率,或下游依赖项自身的限制。 每秒请求数限制不会保护在扇出点出现瓶颈为并发的系统。

    在每个限流实施边界(例如网关、服务、分区或下游依赖项)处,识别最先达到饱和的维度,并在该维度上设置限制。 有关扇出点的并发受限保护,请参阅 舱壁模式,它与节流互为补充。

  • 有意选择限制算法。 将其与要保护的组件的容差匹配。

    算法 行为和最佳拟合度
    令牌存储桶 支持最大达到配置值的突发流量,并以稳定的速率进行补充。 用于需要吸收短时峰值的网关。
    泄漏桶 以恒定速率发出。 用于需要稳定入口速率的后端。
    已修复窗口 易于实现,但在窗口边界处会允许连续突发流量。
    滑动窗口 以增加状态信息为代价,缓解固定窗口的窗口边界问题。
  • 确定该限制适用于谁。 在较粗粒度的边界(例如区域网关)处实施限流时,即使只有其中少数用户造成了负载,也可能影响许多不相关的用户。

  • 确定计数器在一个限制跨越多个节点时驻留的位置。 本地计数器速度很快,但当同一调用方访问多个副本时,会出现计数不足。 共享存储(如 Redis)中的集中式计数器可看到每个请求,但会为每个决策增加延迟。 若要近似实现全局速率限制,请将限制额度分摊到各个副本,并定期进行校准。

  • 快速做出限流判断。 系统必须检测负载上升、反应和负载缓解后恢复正常。 此过程需要持续性能检测。

  • 主动卸载负载,而不是等到濒临崩溃时才采取行动。 只有在组件饱和后才拒绝请求的限流机制,会导致调用方感受到任何背压之前,延迟就已飙升。

    随着利用率接近硬性限制,开始拒绝越来越多的请求。 提前拒绝信号会促使调用方回退,并防止突兀的限制常常引发的延迟雪崩。 将相对于你的 SLO 的 p99 延迟作为主要触发条件。 平均利用率看起来可能正常,但 p99 已经超出阈值。

    在能够区分请求价值的情况下,优先丢弃价值较低或更适合重试的工作负载。 有关详细信息,请参阅 优先级队列模式

  • 返回一个状态代码,用于向客户端表明临时拒绝何时是速率限制导致的:

    • HTTP 429 (请求过多): 调用方超过定义的窗口配置的请求速率。
    • HTTP 503 (服务不可用): 由于出现意外的负载高峰,服务现在无法处理请求。

    包含一个 Retry-After HTTP 标头,以便客户端可以选择重试策略。 返回足够的上下文,让调用方故意重试,而不是猜测。 例如,命名调用方超出的限制、阐明受影响的范围或建议成功的速率。 没有说明原因的拒绝无助于来电者作出调整。

  • 传播来自依赖项的过载信号,而不要自行吸收。 对其调用方实施限流的服务,也必须遵守其自身下游依赖项返回的限流响应。 如果您的服务通过静默重试,或返回通用的 HTTP 500(内部服务器错误)响应来掩盖下游的 429 或 503 响应,那么调用方就无法主动降低请求速率,重试会被放大,过载也会逐级向上游蔓延。 Retry Storm 反模式描述了此失败模式。 将背压传递给上游调用方,以便整个调用链共同卸载负载。

  • 让拒绝的成本低于它所避免的工作成本。 如果拒绝请求需要大量的身份验证、深度分析或复杂的策略评估,则拒绝的请求大量仍会饱和系统。 尽可能早地在请求管道中拒绝,并负载测试拒绝路径本身。

  • 为限流无法为自动扩缩容争取到足够时间的情况做好规划。 如果需求增长速度快于新增产能上线速度,即使是被限流的系统也可能失效。 如果结果不可接受,请保留更大的容量储备并配置更具攻击性的自动缩放。

  • 不要将缓存用作限流的替代方案。 缓存会降低源的平均负载,但不绑定峰值负载。 每次缓存未命中都会回源到源站,而当某个热点键在高并发流量下过期时,许多请求方可能会竞相回填该键对应的缓存项。 使用缓存来降低常规负载,并通过限流来控制最坏情况下的影响。 有关详细信息,请参阅 Cache-Aside 模式

  • 规范化不同操作的资源成本,因为它们通常不具有相同的执行成本。 例如,读取操作的限制可能更高,写入操作的限制可能更低。 忽略每操作成本可能会耗尽容量并创建攻击途径。

  • 使限流配置可在运行时更改。 当出现异常流量时,你需要在无需部署的情况下调整限额。 在事件发生期间,部署速度缓慢且有风险。 外部配置存储模式外部化配置,以便在运行时对其进行更改。

  • 考虑自适应限制,而不是静态限制。 某些限流 SDK 会根据延迟或队列深度信号作出响应,从而使限制值能够反映组件的实际状态。 始终将自适应限制器与设置最大值配对。

  • 随着工作负荷的发展,重新审视限制。 自适应限制器无法跟踪所有类型的漂移,例如 SLO 变化、依赖项容量的变化或每次操作成本的变化。 安排操作员基于这些输入定期进行评审。

何时使用此模式

使用此模式:

  • 将系统保留在其 SLO 内。

  • 防止单个租户垄断应用程序资源。

  • 处理活动量激增。

  • 限制系统所需的最大资源级别。

  • 在高网格碳强度期间减少低值计算。

工作负载设计

评估如何在工作负载设计中使用限制模式,以实现 Azure 良好架构框架支柱中所述的目标并遵循其原则。 下表提供有关此模式如何支持每个支柱目标的指南。

支柱 此模式如何支持支柱目标
可靠性 设计决策有助于工作负荷在发生故障后 复原 ,并确保它在发生故障后 恢复到 正常运行状态。 可以设计限制来帮助防止可能导致故障的资源耗尽。 还可以将此模式用作正常降级计划中的控制机制。

- RE:07 自我保护
安全设计决策有助于确保工作负荷数据和系统的机密性完整性可用性 可以设计限制来帮助防止由于自动滥用系统而导致的资源耗尽。

- SE:06 网络控制措施
- SE:08 强化资源
成本优化 侧重于 维持和改善 工作负荷的 投资回报 强制限制可以通知成本建模,并且可以直接绑定到应用程序的业务模型。 它们还为利用率设定了明确的上限,这可以考虑到资源的大小。

- CO:02 成本模型
- CO:12 扩展成本
通过缩放、数据和代码的优化,性能效率可帮助工作负荷高效地满足需求 当系统处于高需求下时,此模式有助于缓解可能导致性能瓶颈的拥塞。 还可以使用它来主动避免噪声邻居场景。

- PE:02 容量规划
- PE:05 缩放和分区

如果此模式在某个支柱中引入权衡取舍,请将它们与其他支柱的目标进行对比。

Example

下图显示了多租户系统中的限流。

显示多租户应用程序中节流的图示。

左侧标注的三个用户代表多租户 Surveys 应用程序的租户:Adatum、Fabrikam 和 Contoso。 每个用户通过特定于租户的自定义域发送请求,应用程序使用该域来标识租户。 Adatum 通过 surveys.adatum.com 每秒发送 5 个请求,Fabrikam 通过 surveys.fabrikam.com 每秒发送 10 个请求,Contoso 通过 surveys.contoso.com 每秒发送 150 个请求。 在右侧,调查应用程序 Web 角色计量每个租户的每秒请求速率。 Adatum 和 Fabrikam 的请求流量会传递到该应用程序。 Contoso 请求流因以下错误而被阻止:受限制响应,因为速率超过了每个租户的上限。

来自多个租户组织的用户访问云托管的应用程序,以填写和提交调查。 该应用程序包含用于监控各租户用户提交请求速率的监测机制。

为了防止一个租户降低其他租户中的用户的响应能力和可用性,应用程序会限制任何单个租户可以提交的每秒请求数。 应用程序会阻止超出了此限制的请求。

后续步骤