发布于 2026-01-06 0 阅读
0

无服务器时代的编舞与管弦乐编排

无服务器时代的编舞与管弦乐编排

替代文字

编排和协调是微服务架构中的两种交互模式。

在业务编排中,有一个控制器(即“编排器”),它控制着服务之间的交互。它规定了业务逻辑的控制流程,并负责确保所有操作按计划进行。这遵循请求-响应模式。

在编舞中,每个服务都独立运行。它们之间没有硬性依赖,仅通过共享事件松散耦合。每个服务监听自己感兴趣的事件并执行相应的操作。这遵循事件驱动范式。

一如既往,两者并无优劣之分。根据具体情况,其中一种可能比另一种更合适。由于 Lambda 本身本质上是事件驱动的,因此编排式方法在无服务器社区中变得非常流行。我本人非常喜欢这种方法,并使用 EventBridge、SNS 和 Kinesis 等服务构建了许多事件驱动系统。

然而,在这篇文章中,我想谈谈什么时候这样做不好,以及什么时候应该考虑采用编排方法。

简而言之,在实现工作流时,你应该优先在微服务的限界上下文中进行编排,但优先在限界上下文之间进行协调

编舞方法

假设你正在开发一个外卖订餐服务,顾客可以通过该服务从他们最喜欢的餐厅订购外卖。一个典型的订餐流程可能包含以下五个步骤。

我们可以将这五个步骤建模为事件:

  • order_placed
  • restaurant_notified
  • order_accepted
  • user_notified
  • order_completed

通过这些事件,我们可以使用事件驱动的方法来实现订单流程。

  1. 顾客下单。
  2. place-order该函数发布一个order_placed事件。
  3. notify-restaurant该函数由事件触发order_placed
  4. notify-restaurant该功能通过社交网络服务向餐厅发送消息。
  5. notify-restaurant该函数发布一个restaurant_notified事件。
  6. 餐厅通过其手机应用程序收到新订单通知。
  7. 餐厅Accept Order在应用程序中点击,该应用程序会调用ordersAPI。
  8. accept-order该函数发布一个order_accepted事件。
  9. notify-user该函数由事件触发order_accepted
  10. notify-user该功能会向客户发送订单确认邮件。
  11. notify-user该函数发布一个user_notified事件。
  12. 顾客看到订单确认信息后,正焦急地等待食物送达。
  13. 餐厅将食物送到顾客手中。
  14. 餐厅Complete Order在应用程序中点击确认按钮以确认订单已送达。此操作会调用orders应用程序接口 (API)。
  15. complete-order该函数发布一个order_completed事件。

每个功能都完全独立运作。它们都没有整体订单流程的概念,各自只关心:

  • 他们对哪些活动感兴趣?
  • 他们应该怎么做?
  • 他们完成任务后应该发布哪些事件?

优点

  • 流程中的每个步骤都可以独立更改。
  • 流程中的每个步骤都可以独立缩放。
  • 没有单一故障点。
  • 其他系统可以利用这些事件——例如,某个promo-code服务可能对该事件感兴趣order_completed,并向客户发送折扣券。
  • 这些事件本身就是有用的数据,可以输入到数据湖中以生成商业智能报告。

缺点

  • 端到端的监控和报告很困难。
  • 超时机制难以实现。
  • 订单流程并未被显式建模,它仅作为系统运行的涌现属性而存在。因此,它只存在于对系统有完整理解的人的心理模型中。

从商业角度来看,这也引出了一个问题:“这些真的是独立的流程吗?还是同一个流程中的不同步骤?”

对于这种对业务至关重要的工作流程,难道你不希望有人或团队负责并维护它吗?当出现问题,导致每小时损失数百万美元时,你希望看到一屋子人面面相觑,因为没有人真正了解整个流程吗?

如果公司里很少有人了解这种关键流程是如何运作的,那么一旦这些人离开公司,就会给企业带来生存风险。

编排方法

为了实现编排方法,我可能会使用 Step Functions 之类的工具,并将订单流程建模为状态机。

值得注意的是,虽然我们不再需要使用事件来触发订单流程的下一步,但这些事件本身仍然是有用的信息。因此,我们应该从Task状态机的各个状态发布这些事件。例如,在Notify User状态通过 SES 通知用户之后,Task也应该发布该user_notified事件。

这意味着我们仍然可以将订单流程与其他业务部门解耦,这些业务部门可以基于与订单相关的事件构建功能。上述promo-code服务仍然可以像以前一样依赖于该order_completed事件。

优点

  • 由于 Step Functions 提供了内置的可视化和审计历史记录功能,因此端到端监控和报告变得非常简单。
  • 是否容易实现超时功能?例如,餐厅接受订单的超时时间,或者订单的总时长。
  • 业务逻辑集中在一个地方,易于维护和管理。
  • 订单流程已建模并进行源代码控制。您可以在 Step Functions 控制台中直接查看。
  • 订单流程已建模并进行源控制。是的,这一点非常重要,足以算作两个优点!

缺点

  • 又得学一项AWS服务了。
  • Step Functions 的服务价格昂贵,每百万次状态转换收费 25 美元(Start而且百万次状态转换是要计算的)。End
  • 如果 Step Functions 服务宕机,则无法处理任何订单。Lambda、EventBridge 或任何对订单流程至关重要的服务也会出现同样的问题。

混合方法

在限定的上下文中,我承担着与特定业务领域相符的一系列职责。而且,我希望这些职责都集中在少数几个组件上,以便我能同时理解它们。由于它们协同工作以实现某些特定的业务功能(例如处理支付),因此它们构成了一个高度内聚的整体。并且,由于我拥有这个微服务限定上下文中的所有内容,只要不违反与外部服务的约定,我可以自由地更改和重组这些内容。

我经常看到在限定的上下文中,工作流程通过 SQS/SNS/EventBridge 中的消息进行协调。

总的来说,我认为这不是个好主意。

我喜欢用事件以松耦合的方式将不同的服务集成在一起。但我认为在同一个限界上下文中这样做就不是个好主意,因为工作流并非作为一个独立的概念存在,也没有被明确地捕获和进行源代码控制。

在这些精心编排的工作流程中,工作流程仅仅是松散连接的函数的总和。正如我们前面在送餐示例中讨论的那样,这使得它们很难进行推理和调试。即使是像工作流程级别的超时这样简单的功能,或者任务级别的任务(例如,如果餐厅在 10 分钟内未接受或拒绝订单,则订单超时),也很难轻松实现。

如果这就是您目前的情况,您应该考虑将这些工作流程迁移到 Step Functions。

但是,在限定的上下文中,我会通过 SNS/EventBridge/Kinesis 等发布和订阅事件。这样,更大系统的不同部分就可以保持松耦合,只依赖于彼此的事件,并且可以独立地演进和失败。

编排和协调并非必须相互排斥。每当我在状态机中引入状态变更(例如将订单状态从“已pending完成”更改为“未完成processed”)时,我都会将这些状态变更发布为事件。其他服务可以监听并响应这些状态变更,从而实现协调。

最后,我想分享一下我在实现业务工作流程方面的经验法则:在微服务的限界上下文中使用编排,但在限界上下文之间使用协调。

这篇文章《无服务器环境下的编舞与管弦乐编排》最初发表于theburningmonk.com

文章来源:https://dev.to/aws-heroes/choreography-vs-orchestration-in-the-land-of-serverless-f58