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

基于队列的负载调节模式

使用一个在任务与其调用的服务之间充当缓冲区的队列。 这种方法可以平缓间歇性的高负载,这类负载可能会导致服务失败或任务超时。它有助于尽可能减少需求高峰对任务和服务的可用性及响应速度的影响。

上下文和问题

云中的许多解决方案运行调用服务的任务。 在此环境中,间歇性繁重的负载可能会导致服务出现性能或可靠性问题。

服务可能是与使用服务的任务相同的解决方案的一部分,也可能是提供对常用资源的访问的合作伙伴服务。 这些类型的服务示例包括缓存或存储服务。 当多个任务并发运行并使用同一服务时,随时难以预测请求量。

服务可能会遇到重载需求高峰,并使服务无法快速响应请求。 如果服务无法处理这些请求造成的争用,那么大量并发请求涌入也可能导致服务发生故障。

解决方案

将队列置于任务和服务之间。 任务和服务异步运行。 该任务会向队列发送一条包含服务所需数据的消息。 队列充当缓冲区并存储消息,直到服务检索消息。 服务从队列中检索消息并对其进行处理。 可以通过同一消息队列将来自多个任务的请求(可以通过高度可变速率生成)传递到服务。 下图显示了队列如何对服务上的负载进行调配。

显示消息队列如何充当任务和服务之间的缓冲区的关系图。

队列将任务与服务分离,以便即使并发任务生成大量请求,服务也能以自己的速度处理消息。 此外,即使服务在向队列发布消息时不可用,任务也不会延迟。

此模式具有以下优点:

  • 它有助于最大程度地提高可用性,因为服务延迟不会立即直接影响应用程序。 即使服务不可用或当前未处理消息,应用程序也可以继续将消息发布到队列。

  • 它有助于最大程度地提高可伸缩性,因为队列数和服务数可能因需求而异。

  • 它有助于控制成本,因为你只需要足够的服务实例来满足平均负载而不是峰值负载的要求。

Note

某些服务在需求达到可能导致系统故障的阈值时实现限制。 限流可能会导致部分功能不可用。 在这些服务中实现负载调配,以确保需求未达到此阈值。

问题和注意事项

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

  • 实现用于控制服务处理消息速率的应用程序逻辑,以避免目标资源过载。 避免将需求高峰带到系统的下一阶段。 在负载条件下测试系统,以确保其提供所需的均衡。 若要实现所需的调配,请调整队列数和处理消息的服务实例数。

  • 消息队列是一种单向通信机制。 如果任务需要来自服务的答复,则可能需要实现服务可用于发送响应的机制。 有关详细信息,请参阅 Azure0 中的异步消息传送选项。

  • 如果不限制消费者的总体下游请求速率,自动扩缩容只会把过载转移到下游依赖项。 这种过载可能会加剧这些服务之间对共享资源的争用,并降低队列平衡负载的效果。

  • 如果平均生成者速率超过使用者速率,队列会继续增长并增加延迟。 监控队列深度,并在安全范围内扩缩消费者,或在生产者端削减负载。

  • 此模式取决于队列持久性,以防止消息丢失。 如果中转站不将消息保存到持久存储,崩溃或容量限制可能会导致排队数据在使用者处理之前丢失。 选择将消息保存到磁盘或复制存储的队列服务,并了解其大小配额和保留限制。 对于需要消息才能生存区域故障的工作负荷,请评估异地灾难恢复选项。

  • 大多数队列服务提供至少一次语义的消息,这意味着使用者可以多次接收相同的消息。 将使用者逻辑设计为 幂等 ,以便多次处理同一消息会产生相同的结果,并避免重复记录或重复收费等问题。

  • 某些消息无法处理,因为它们包含格式不正确的数据、引用缺少的资源或触发永久性错误。 与其让这些消息无限期循环并阻止队列,而是将它们路由到 死信队列。 监控死信队列深度,以便运维团队调查失败原因、修复根本问题,并在适当时重新提交消息。

  • 在生产者和消费者之间引入队列,并不能在所有情况下保留原始提交顺序,尤其是在多个消费者并行处理消息时。 如果工作负荷需要严格的排序,请在Azure 服务总线中使用 message 会话等功能。 如果不要求严格顺序,请将消费者设计为能够按任意顺序处理消息,这样可以简化扩展。

何时使用此模式

在以下情况下使用此模式:

  • 你的工作负载会出现间歇性峰值,可能会压垮下游服务。

  • 需要将请求引入与处理吞吐量分离,以提高复原能力和成本控制。

在以下情况下,此模式可能不适用:

  • 调用方需要低延迟、同步响应。

  • 工作负荷量可预测较低且稳定,因此添加排队复杂性几乎没有好处。

工作负载设计

评估如何在工作负载设计中使用基于队列的负载调节模式,以满足 Azure Well-Architected Framework 支柱中涵盖的目标和原则。 下表提供有关此模式如何支持每个支柱目标的指南。

支柱 此模式如何支持支柱目标
可靠性 设计决策有助于工作负荷在发生故障后 复原 ,并确保它在发生故障后 恢复到 正常运行状态。 此模式描述的方法可以通过将任务的到达与其处理分离,从而针对需求突然激增提供复原能力。 它还可以在队列处理中隔离故障,使其不会影响摄入量。

- RE:06 扩展
成本优化 侧重于 维持和改善 工作负荷的 投资回报 由于负载处理与请求或任务接收分离,因此可以使用这种方法来减少为处理峰值负载而过度预配资源的需要。

- CO:12 扩展成本
通过缩放、数据和代码的优化,性能效率可帮助工作负荷高效地满足需求 此方法可实现对吞吐量性能的有意设计,因为请求引入不需要与处理速率相关联。

- PE:05 缩放和分区

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

Example

Web 应用将数据写入外部数据存储。 如果 Web 应用的多个实例并发运行,则数据存储可能无法足够快地响应请求,从而导致请求超时、被限制或失败。 下图显示了某个数据存储被应用程序多个实例发起的并发请求压垮。

显示来自 Web 应用实例的多个并发请求使服务不堪重负的示意图。

若要解决此问题,请使用队列来调配应用程序实例和数据存储之间的负载。 Azure Functions应用从服务总线队列读取消息,并向数据存储执行读/写请求。 Azure Functions 可以在你配置的缩放范围内,通过使用基于目标的缩放,根据 服务总线 的积压情况扩展实例。 还可以优化触发器并发设置以保护数据存储。 有关实施指南,请参阅 基于目标的缩放限制横向扩展。如果不进行这种调优,工作器层可能会再次引发后端争用。

显示如何使用队列和函数应用来调配负载的关系图。

作为技术变体,可以使用Azure 容器应用而不是Azure Functions来实现相同的模式。 在这种方法中,容器化工作器消费来自 服务总线 的消息,并将其写入数据存储。 容器应用会根据与队列相关的缩放规则,将工作器扩缩到已配置的最小副本数和最大副本数之间。 还可以使用 Azure 队列存储 作为事件源来实现相同的方法。 有关实现指南,请参阅 在容器应用中设置缩放规则并使用容器应用部署事件驱动的作业

后续步骤

实现此模式时,以下指南可能也比较有用:

  • Azure 中的异步消息传送选项:消息队列本质上是异步的。 如果任务直接与服务通信,则可能需要重新设计任务的应用程序逻辑。 同样,可能需要重构服务以接受来自消息队列的请求。

  • Azure消息服务之间的选择:获取详细信息以帮助你在 Azure 应用程序中选择消息传递和队列机制。

  • 开发后台作业的建议:将此模式应用于后台作业,以便在应用程序遇到高负载时,消息队列可以存储后台任务的请求。

  • Web-Queue-Worker 架构风格:Web 和工作进程都是无状态的。 会话状态可以存储在分布式缓存中。 辅助角色异步执行长时间运行的任务,并且可以由队列中的消息触发,也可以按计划运行以进行批处理。

  • 竞争使用者模式:可能运行服务的多个实例,每个实例充当负载调配队列中的消息使用者。 可以使用此方法来调整接收消息并将其传给给服务的速度。

  • 限制模式:在服务中实现限制的一种简单方法是使用基于队列的负载调配,并通过消息队列将所有请求路由到服务。 该服务可以按速率处理请求,以确保它不会耗尽所需的资源,并减少可能的争用量。