前提
考虑一下一家外卖公司 应用程序,它是一个在线食品配送应用程序。应用程序的客户端通过发出 HTTPPOST /orders请求来创建订单,并期望在 600 毫秒内得到响应。由于 该 应用程序使用微服务架构,实现订单创建的职责分散在多个服务中。POST请求首先被路由到,然后Order Service它必须与以下服务协作:
- Restaurant Service- 了解餐厅的菜单和价格
- Consumer Service- 知道下Consumer订单的状态
- Kitchen Service- 创建一个Ticket,告诉厨师要做什么
- Accounting Service- 授权消费者的信用卡
Order Service可以使用同步请求/响应来调用这些服务中的每一个。例如,它可能使用 REST 或 gRPC 实现服务间通信。
然而,使用同步请求/响应的一个主要缺点是它降低了可用性。这是因为如果 的任何Order Sevice合作者不可用,它将无法创建订单并且必须向客户端返回错误。
另一种方法是Order Service通过使用 CQRS 和 Saga 模式消除其及其协作者之间的所有同步通信。Order Service可以使用CQRS模式来维护餐厅菜单的副本,从而消除从Restaurant Service. 它可以使用Saga 模式异步验证订单。Order Service创建一个Order状态PENDING并返回一个响应给POST /order。然后它通过与其他服务进行异步通信来完成订单的创建。
这种方法的一个主要好处是它提高了可用性。即使其他服务之一不可用,也始终Order Service响应请求。POST /orders然而,使用 saga 完成订单创建的一个缺点是,对 saga 的响应POST不会告诉客户订单是否被批准。客户端必须通过定期调用来找出答案GET /orders/{orderId}。
问题
在处理同步请求时,服务应该如何与其他服务协作?
概念
- 微服务架构通常将处理请求的责任分配给多个服务
- 通常要求操作具有高可用性和低响应时间
- 操作的可用性是处理请求时调用的服务的可用性的乘积: serviceAvailability numberOfSynchronouslyCollaboratingServices
- 服务可以重试对失败的协作者的请求,但这会增加响应时间。
解决方案
设计一个服务,以便它可以响应同步请求,而无需等待任何其他服务的响应。
使服务自包含的一种方法是将所需的功能实现为服务模块而不是单独的服务。例如,我们可以合并Order Serviceand Restaurant Service。
使服务自包含的另一种方法是使用CQRS和Saga模式与其他服务协作。自包含服务使用 Saga 模式异步维护数据一致性。它使用 CQRS 模式来维护其他服务拥有的数据的副本。
示例
Order Service前面描述的 该 应用程序中的 是自包含服务的示例。createOrder()例如,该操作会查询拥有的数据的 CQRS 副本Restaurant Service以验证订单并为其定价,然后启动 saga 以完成订单的创建。
总结
这种模式有以下好处:
- 提高可用性和响应时间
这种模式有以下缺点:
- 使用 CQRS 的成本和复杂性增加
- 增加使用 sagas 的复杂性
- 使用 sagas 时不太直接的 API
- 由于在服务中实现功能而不是作为单独的服务实现更大的服务