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

使用者竞争模式

使多个并发使用者能够处理同一消息通道上收到的消息。 借助多个并发使用者,系统可以同时处理多个消息,以优化吞吐量、提高可伸缩性和可用性,并平衡工作负荷。

上下文和问题

云应用程序通常处理大量请求。 应用程序可以通过消息传递系统将请求传递给以异步方式处理请求的使用者服务,而不是同步处理每个请求。 此策略有助于防止请求处理阻止应用程序业务逻辑。

请求数可能会随时间而显著变化。 用户活动或来自多个租户的聚合请求突然增加可能会产生不可预知的工作负荷。 在高峰时段,系统可能需要每秒处理数百个请求。 在其他情况下,该数字可能很小。 此外,处理这些请求所需的工作可能会有所不同。 如果使用单个消费者服务实例,请求可能会使该实例超载。 或者,应用程序消息的涌入可能会重载消息系统。

为了处理这种波动的工作负荷,系统可以运行多个使用者服务实例。 但是,系统必须协调这些使用者,以确保每个消息仅传递到一个使用者。 系统还必须平衡使用者之间的工作负荷,以防止一个实例成为瓶颈。

解决方案

使用消息队列实现应用程序与使用者服务实例之间的信道。 应用程序将请求作为消息发送到队列,使用者服务实例从队列接收和处理消息。 此方法允许同一个使用者服务实例池处理来自应用程序的任何实例的消息。 下图显示了消息队列如何将工作分配到服务实例。

显示消息队列如何向服务实例分配工作的关系图。

注释

多个使用者接收这些消息,但竞争使用者模式不同于 Publisher-Subscriber 模式。 在竞争消费者模式中,每个消息由一个消费者接收并处理。 在 Publisher-Subscriber 模式中, 所有 使用者都接收 条消息。

此解决方案具有以下优点:

  • 它提供了一个负载均衡系统,能够处理来自应用程序实例的请求量的剧烈波动。 队列充当应用程序实例与使用者服务实例之间的缓冲区。 此缓冲区可以最大程度地降低应用程序实例和服务实例的可用性和响应能力的影响。 有关详细信息,请参阅基于队列的负载调控模式。 需要长时间处理的消息不会阻止其他消费者服务实例同时处理其他消息。

  • 它提高了可靠性。 如果生成者直接与使用者通信,而不是使用此模式,并且不监视使用者,则当使用者发生故障时,它面临丢失消息或无法处理它们的高概率。 在此模式中,系统不会向特定服务实例发送消息。 失败的服务实例不会阻止生成者,任何工作服务实例都可以处理消息。

  • 它不需要在使用者之间或生成者和使用者实例之间进行复杂的协调。 消息队列可确保每条消息至少传送一次。

  • 它会缩放。 应用 自动缩放时,当消息量波动时,系统可以动态增加或减少使用者服务实例的数量。

  • 如果消息队列提供事务读取操作,则可以提高复原能力。 如果使用者服务实例读取并处理作为事务操作的一部分的消息,并且失败,则此模式可以确保消息返回到队列,以便另一个使用者服务实例可以处理它。 若要降低连续消息失败的风险,建议使用 死信队列

问题和注意事项

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

  • 消息排序: 不保证使用者服务实例接收消息的顺序,不一定显示消息的创建顺序。 设计系统,使其以 幂等方式处理消息。 此设计有助于消除处理订单依赖关系。

    Azure 服务总线可以使用 消息会话实现消息和其他模式的有保证先出顺序。

  • 服务复原要求: 如果系统检测到并重启失败的服务实例,则可能需要将这些服务实例执行的操作实现为幂等的,以最大程度地减少在检索和处理单条消息时的影响。

  • 病毒消息检测: 格式不正确的消息或需要访问不可用的资源的任务可能会导致服务实例失败。 系统应防止这些消息无限期返回队列,并在必要时捕获并存储其详细信息。 服务总线可以在传递计数超过配置的阈值后自动将消息发送到MaxDeliveryCount

  • 结果处理: 处理消息的服务实例与生成消息的应用程序逻辑完全分离,因此它们可能无法直接通信。 如果服务实例生成必须返回到应用程序逻辑的结果,请将此信息存储在两个组件可以访问的位置。 若要防止应用程序逻辑检索不完整的数据,系统必须在处理完成时指示。 工作进程可以通过专用消息回复队列将结果传回应用程序逻辑。 应用程序逻辑必须能够将这些结果与原始消息相关联。

  • 消息传递系统缩放: 在大规模解决方案中,高消息量可能会使单个消息队列不堪重负,并将其变成系统瓶颈。 在这种情况下,请考虑将消息系统分区为将消息从特定生成者发送到特定队列,或进行负载均衡,以便跨多个消息队列分配消息。

  • 消息传送系统可靠性: 使用可靠的消息传送系统保证在应用程序排队后不会丢失消息。 此功能对于确保所有消息至少传递一次至关重要。

何时使用此模式

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

  • 应用程序工作负荷分为可以异步运行的任务。

  • 任务是独立的且可并行运行。

  • 工作量高度可变,需要可缩放的解决方案。

  • 解决方案必须提供高可用性,并在任务处理失败时保持复原能力。

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

  • 无法轻松地将应用程序工作负荷分离为离散任务,或者任务之间高度依赖。

  • 任务必须同步运行,应用程序逻辑必须等待每个任务完成,然后才能继续。

  • 任务必须在特定序列中运行。

注释

某些消息传送系统支持会话,使生成者将消息分组在一起,并确保同一使用者处理组中的所有消息。 当支持强制消息排序并将消息按顺序从生成者传递到单个使用者时,可以将此机制与优先消息一起使用。

工作负载设计

评估如何在工作负载的设计中使用竞争消费者模式来实现 Azure Well-Architected Framework 支柱中涵盖的目标和原则。 下表提供有关此模式如何支持每个支柱目标的指南。

支柱 此模式如何支持支柱目标
可靠性 设计决策有助于工作负荷在发生故障后 复原 ,并确保它在发生故障后 恢复到 正常运行状态。 此模式通过将使用者视为副本来生成队列处理中的冗余,因此实例故障不会妨碍其他使用者处理队列消息。

- RE:05 冗余
- 后台作业
成本优化 侧重于 维持和改善 工作负荷的 投资回报 此模式有助于优化成本,因为它根据队列深度进行缩放,并在队列为空时缩减为零。 它还可以优化成本,因为可以限制并发使用者实例的最大数量。

- CO:05 速率优化
- CO:07 组件成本
通过缩放、数据和代码的优化,性能效率可帮助工作负荷高效地满足需求 此模式在使用者节点之间分配负载以提高利用率,并根据队列深度动态缩放将最大程度地减少过度预配。

- PE:05 缩放和分区
- PE:07 代码和基础结构

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

示例

Azure 提供服务总线队列和 Azure Functions 队列触发器,这些触发器直接实现此云设计模式。 Functions 通过触发器和绑定与 Service Bus 集成。 通过此集成,可以生成使用发布者队列消息的函数。 发布应用程序将消息发布到队列,实现为 Functions 的使用者可以检索和处理这些消息。

对于高可用性,服务总线队列允许消费者在从队列检索消息时使用 PeekLock 模式。 此模式保留消息,但会将其隐藏给其他使用者。 Functions 运行时在 PeekLock 模式下接收消息。 如果函数成功完成,运行时将调用消息上的 Complete。 如果函数失败,运行时可能会再次调用 Abandon 消息并使其可见,以便另一个使用者可以检索它。 如果函数的运行时间超过 PeekLock 超时,则只要函数运行,运行时就会自动续订锁。

使用服务总线将工作分发到 Functions 的图示。

函数根据 队列深度和流量自动缩放使用者实例数。 这种缩放可让解决方案处理工作量激增,同时在低容量期间将成本降到最低。 如果 Functions 创建多个实例,则它们通过独立拉取和处理消息进行竞争。 有关详细信息,请参阅 服务总线队列、主题和订阅适用于 Functions 的服务总线触发器

有关如何使用适用于 .NET 的服务总线客户端库将消息发送到服务总线队列的详细信息,请参阅已发布 的示例

后续步骤

  • 在 Azure 中选择消息传送服务:了解不同的 Azure 消息传递服务(例如服务总线、Azure 存储队列、Azure 事件中心和 Azure 事件网格)如何支持异步通信模式,以及如何为方案选择正确的服务和消息传送模型。

  • 自动缩放最佳做法:了解如何设计基于工作负荷(如队列长度或消息吞吐量)横向扩展使用者实例的解决方案,以便在低活动期间处理峰值负载和控制成本。

  • 计算资源合并模式:可以将使用者服务的多个实例合并到单个进程中,以降低成本和管理开销。 计算资源合并模式描述了此方法的优点和权衡。

  • 基于队列的负载调配模式:消息队列可以向系统添加复原能力。 服务实例的弹性使其能够处理来自应用程序实例的多变请求量。 消息队列作为缓冲区,用于平衡负载。 有关此方案的详细信息,请参阅基于队列的负载调节模式。