你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
让每个服务自行决定何时以及如何处理业务操作,而不是依赖中央编排器。 此方法分散工作流逻辑,并在系统组件之间分配职责。
上下文和问题
通常将基于云的应用程序划分为多个小型服务,这些服务共同处理端到端业务事务。 事务中的单个操作可能会导致所有服务之间的多个点到点调用。 理想情况下,这些服务是松散耦合的。 设计分布式、高效且可缩放的工作流非常困难,因为它涉及复杂的服务间通信。
常见的通信模式是使用集中式服务或编排器。 在将操作委派给相应的服务时,传入的请求流经编排器。 每个服务完成它们的职责,却不知晓整个工作流程。
通常将业务流程协调程序模式实现为自定义软件,该软件具有有关系统中服务职责的域知识。 此方法的一个好处是,业务流程协调程序可以根据下游服务执行的各个操作的结果来合并事务的状态。
这种方法也造成了一些障碍。 添加或删除服务可能会破坏现有逻辑,因为需要重新连接通信路径的某些部分。 这种依赖关系使得编排器实现变得复杂且难以维护。 编排器可能会对工作负荷的可靠性产生负面影响。 在负载下,它可以引入性能瓶颈,并成为单一故障点(SPoF)。 当业务流程协调程序失败或过载时,故障可以传播到所有依赖的下游服务。
解决方案
在服务之间委托交易处理逻辑。 让每个服务参与业务运营的通信工作流,并决定何时以及如何处理它。
编舞模式可最大程度地减少对集中通信工作流的自定义软件的依赖关系。 组件在协调彼此的工作流程时实现通用逻辑,而无需直接相互通信。
实现编排的一种常用方法是使用消息中转站来缓冲请求,直到下游组件声明并处理它们。 下图显示了通过 发布者-订阅者模型处理请求。
客户端请求作为消息在消息代理中排队。
服务或订阅者轮询代理,以确定它是否可以基于其实现的业务逻辑处理该消息。 中转站还可以将消息推送到对该消息感兴趣的订阅者。
每个订阅的服务都按照消息指示执行其操作,并向中转站反馈操作成功或失败的消息。
如果操作成功,服务可以将消息发布到同一队列或其他消息队列,以便另一个服务可以根据需要继续工作流。 如果操作失败,服务将发布失败消息。 订阅该消息的服务可以针对失败的操作或整个事务运行预定义补偿操作。
问题和注意事项
在决定如何实现此模式时,请考虑以下几点:
故障处理的复杂性。 应用程序中的组件可以管理原子任务,并依赖于系统的其他部分。 一个组件中的失败可能会影响其他组件,这可能会导致完成总体请求的延迟。
若要正常处理故障,请实现故障处理逻辑,这会带来复杂性。 故障处理逻辑(如 补偿事务)也容易发生故障。
顺序进程。 此模式适用于并行处理独立业务操作的工作流。 当编排需要按顺序进行时,工作流程可能会变得复杂。 例如,服务 D 只能在服务 B 和服务 C 成功完成其操作后启动其操作。
大规模可观测性。 如果服务数量迅速增长,此模式将带来挑战。 许多独立的移动部件使服务之间的工作流复杂化。 如果没有一个掌握完整事务状态的中央编排器,任何单个组件都无法掌握某个正在执行的业务操作的全貌。 必须一致地使用 分布式跟踪 和关联标识符来维护可观测性。
弹性处理程序通信。 在业务流程协调程序主导的设计中,中央组件可以将复原能力责任(例如暂时性、非转移性和超时故障的重试处理)委托给专用复原处理程序。
在基于编舞的设计中移除编排器后,下游组件不会承担弹性职责。 它们仍然集中在弹性处理程序中。 但下游组件必须直接与该处理程序通信,这会增加点到点通信。
事件架构演变。 事件架构演变可能会随着时间推移导致消费者出现重大变更。 在此模式中,多个独立服务处理相同的事件。 如果生成者更改事件的数据结构,它可以破坏依赖于旧架构的下游使用者。 使用架构注册表管理事件协定,并在服务独立演变时使用向后兼容的演变。
幂等性和事件排序。 至少一次传递和重试可能会产生重复消息,并且并发使用者可能会无序处理消息。 通过跟踪稳定的消息标识符,将消费者设计为具备 幂等性。 当需要有序处理时,请使用代理功能,例如服务总线会话或包含序列或版本数据,让使用者拒绝过时事件并检测间隙。
原子状态和事件发布。 在单独的操作中更新其数据存储并发布事件的服务可以在一个操作失败时提交另一个操作。 使用 事务性 Outbox 模式 或等效的原子机制,先将状态变更和事件一并持久化,再由单独的进程发布该事件。
紧急行为和事件风暴。 分散式事件拓扑可以大规模创建新兴行为。 当许多服务对彼此的事件做出反应时,系统可能会无意中生成反馈循环或事件风暴。 次要事件可能会触发下游反应的级联。 若要防止循环事件链,请使用事件筛选、消费者并发限制、节流和显式规则等防护措施。
何时使用此模式
在以下情况下使用此模式:
下游组件在 激发和忘记 方法中独立处理原子操作。 每个组件完成一个任务,然后通过消息代理向其他组件发出完成信号。 发起服务在调度任务后不会主动管理或跟踪任务,但下游服务仍通过事件传达结果。
你希望经常更新和替换组件。 通过这种模式,可以用更少的努力和最小的对现有服务的干扰来修改应用程序。
将无服务器体系结构用于简单的工作流。 这些组件可以是短期组件,也可以是事件驱动型组件。 事件发生时,服务将创建执行任务的组件,服务会在完成该任务后删除组件。
限界上下文之间的通信需要在领域边界上实现松散耦合。 对于单个限界上下文内的通信,则可改为考虑编排器模式,具体取决于复杂程度和团队偏好。
中央业务流程协调程序引入了性能瓶颈。
在以下情况下,此模式可能不适用:
应用程序很复杂,需要一个核心组件来处理共享逻辑,以保持下游组件轻量级。
组件之间的点到点通信是不可避免的。
需要使用业务逻辑来合并下游组件处理的所有操作。
工作负载设计
评估如何在工作流的设计中使用编舞模式来实现 Azure Well-Architected Framework 支柱中涵盖的目标和原则。 下表提供有关此模式如何支持每个支柱目标的指南。
| 支柱 | 此模式如何支持支柱目标 |
|---|---|
| 卓越运营有助于通过标准化流程和团队凝聚力来实现工作负荷质量。 | 此模式中的分布式组件是自主的,设计为可替换的,因此,你可以修改工作负荷,对系统的总体更改更少。 - OE:04 工具和流程 |
| 通过缩放、数据和代码的优化,性能效率可帮助工作负荷高效地满足需求。 | 当集中式业务流程拓扑中出现性能瓶颈时,此模式提供了一种替代方案。 - PE:02 容量规划 - PE:05 缩放和分区 |
与任何设计决策一样,请考虑此模式可能引入的、与其他支柱目标之间的各种权衡取舍。
示例
此示例通过创建与微服务一起运行函数的事件驱动的云原生工作负荷来演示编舞模式。 当客户端请求寄送包时,工作负载会分配无人机。 包裹准备好由计划无人机取件后,交付过程将启动。 在包运输过程中,工作负载处理交付,直到它收到已发货状态。 有关完整参考架构,请参阅使用 Azure 容器应用 的微服务。
引入服务接收客户端请求,并将其转换为包含传递详细信息的消息。 业务事务在服务使用这些新消息后开始。
单个客户端业务事务需要三个不同的业务操作:
创建或更新包。
分配无人机交付包裹。
处理交付,包括检查和发送包裹发货时的通知。
包裹、无人机调度程序和交付微服务执行业务处理。 服务使用消息传送而不是中心业务流程协调程序相互通信。 每个服务都必须提前实现一个协议,以分散的方式协调业务工作流。
设计
服务通过多个跃点按顺序处理业务事务。 每个跃点在所有业务服务之间共享单个消息总线。
当客户端通过 HTTP 终结点发送传递请求时,引入服务会收到该请求,将其转换为消息,然后将消息发布到共享消息总线。 订阅的业务服务使用添加到总线的新消息。 当业务服务收到消息时,它会成功完成操作,或者请求失败或超时。如果请求成功,服务将使用状态代码响应总线 Ok ,引发新的操作消息,并将其发送到消息总线。 如果请求失败或超时,服务会将失败原因代码上报到消息总线,并通过 Azure 服务总线 将该消息转入死信队列。 该服务还会将无法在特定时间内接收或处理的消息死信。
此设计使用多个消息总线来处理整个业务事务。 Azure 服务总线 和 Azure 事件网格 提供此设计的消息传送服务平台。 工作负荷在Azure 容器应用上运行。 引入服务作为容器应用上托管的Azure函数运行,而包、无人机计划程序以及交付服务在同一容器应用环境中作为微服务运行。 容器应用处理运行业务逻辑 的事件驱动处理 。
此设计还可确保编舞按顺序进行。 单个 服务总线 命名空间包含一个具有两个订阅的主题和一个支持会话的队列。 引入服务将消息发布到主题。 包服务和无人机调度程序服务订阅该主题,并发布通知队列成功请求的消息。 包括一个通用会话标识符,该标识符将 GUID 与传递标识符相关联,以便传递服务可以关联每个事务所需的两条消息。 其中一条消息确认包裹已准备就绪,另一条消息确认无人机已排定。 如果没有基于会话的相关性,则传递服务无法跨独立跃点关联相关消息,因为没有中央协调器跟踪事务状态。 传递服务会等待每个事务的两条相关消息。 第一条消息表明包裹已准备好发货,而第二条消息显示无人机的计划已安排。
在此设计中,服务总线 处理在整个交付过程中不得丢失或重复的高价值消息。 当包裹发货时,状态更改将发布到 Event Grid。 事件发送方对状态更改的处理方式没有期望。 此设计不包括的下游组织服务可以侦听此事件类型并运行特定的业务逻辑,例如向用户发送订单状态电子邮件。
如果在另一个计算服务(如 AKS)中部署此模式,则可以在业务应用程序所在的同一 Pod 中部署 大使 作为 sidecar 。 共置可最大限度地减少通信延迟,但代理会增加处理和资源开销,并且会随着应用程序的扩展而增加。 当你需要平台未提供的、与语言无关的连接性支持时,请使用这种方法。
为了避免导致多次尝试的级联重试操作,业务服务应立即标记不可接受的消息。 使用常见原因代码或定义的应用程序代码来丰富这些消息,以便服务可以将它们移动到 DLQ。 请考虑实施 Saga 模式来管理下游服务的一致性问题。 例如,另一个服务仅通过运行补偿、重试或透视事务来处理死信消息以进行修复。
业务服务具有幂等性,以确保重试操作不会导致资源重复创建。 例如,包服务使用 upsert 操作将数据添加到数据存储。
后续步骤
- 查看 Azure 中的异步消息传送选项 ,了解可用于实现分散式工作流的不同基础结构选择。
相关资源
在编排设计中考虑这些模式:
使用 大使模式 将业务服务通信与消息总线模块化。
实现 Queue-Based 负载均衡模式 来处理工作负荷中的峰值。
通过 Publisher-Subscriber 模式使用异步分布式消息传送。
如果一个或多个相关操作失败,请使用 补偿事务 来撤消一系列已经成功的操作。