无服务器时代的编舞与管弦乐编排
编排和协调是微服务架构中的两种交互模式。
在业务编排中,有一个控制器(即“编排器”),它控制着服务之间的交互。它规定了业务逻辑的控制流程,并负责确保所有操作按计划进行。这遵循请求-响应模式。
在编舞中,每个服务都独立运行。它们之间没有硬性依赖,仅通过共享事件松散耦合。每个服务监听自己感兴趣的事件并执行相应的操作。这遵循事件驱动范式。
一如既往,两者并无优劣之分。根据具体情况,其中一种可能比另一种更合适。由于 Lambda 本身本质上是事件驱动的,因此编排式方法在无服务器社区中变得非常流行。我本人非常喜欢这种方法,并使用 EventBridge、SNS 和 Kinesis 等服务构建了许多事件驱动系统。
然而,在这篇文章中,我想谈谈什么时候这样做不好,以及什么时候应该考虑采用编排方法。
简而言之,在实现工作流时,你应该优先在微服务的限界上下文中进行编排,但优先在限界上下文之间进行协调。
编舞方法
假设你正在开发一个外卖订餐服务,顾客可以通过该服务从他们最喜欢的餐厅订购外卖。一个典型的订餐流程可能包含以下五个步骤。
我们可以将这五个步骤建模为事件:
order_placedrestaurant_notifiedorder_accepteduser_notifiedorder_completed
通过这些事件,我们可以使用事件驱动的方法来实现订单流程。
- 顾客下单。
place-order该函数发布一个order_placed事件。notify-restaurant该函数由事件触发order_placed。notify-restaurant该功能通过社交网络服务向餐厅发送消息。notify-restaurant该函数发布一个restaurant_notified事件。- 餐厅通过其手机应用程序收到新订单通知。
- 餐厅
Accept Order在应用程序中点击,该应用程序会调用ordersAPI。 accept-order该函数发布一个order_accepted事件。notify-user该函数由事件触发order_accepted。notify-user该功能会向客户发送订单确认邮件。notify-user该函数发布一个user_notified事件。- 顾客看到订单确认信息后,正焦急地等待食物送达。
- 餐厅将食物送到顾客手中。
- 餐厅
Complete Order在应用程序中点击确认按钮以确认订单已送达。此操作会调用orders应用程序接口 (API)。 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






