让客户端处理微服务最终一致性的最佳实践

Mer*_*ard 5 eventual-consistency microservices

我一直在阅读一些有关最终一致性和编排微服务的文章和问题,但我还没有看到这个问题的明确答案。我会用通用术语来表达它。

简而言之:如果客户端历史上对您的系统进行了后续同步 REST 调用,那么一旦调用不同的微服务(由于最终一致性),后续调用可能会返回意外结果,您该怎么办?

问题

假设您有一个提供 REST API 的整体应用程序。假设您想要将两个模块 A 和 B 转换为微服务。B维护的实体可以引用A维护的实体(例如A维护学生,B维护班级)。在单体情况下,模块仅引用相同的数据库,但在微服务情况下,它们各自拥有自己的数据库并通过异步消息进行通信。所以他们的数据库最终是相互一致的。

我们 API 的一些现有第三方客户端应用程序习惯于首先(同步)调用属于模块 A 的端点,并在第一个调用返回后,立即(即几毫秒后)调用模块 B 中的端点作为其调用的一部分。工作流程(例如创建一个学生并将其放入班级)。在新的情况下,这会导致一个问题:当第二次调用发生时,模块 B 可能还没有意识到模块 A 中的更改。因此客户端应用程序的现有工作流程可能会中断。(例如,模块 B 可能会回应:您尝试放入班级的学生不存在,或者年级错误。)

当人类用户通过某些前端应用程序单独完成调用时,这不是一个大问题,因为无论如何,模块通常在一秒钟后就会保持一致。当客户端应用程序(不受我们控制)只是调用 A,然后立即调用 B 作为自动化工作流程的一部分时,就会出现问题。在这种情况下,最终一致性根本不够快。

描述情况的简单图表

问题

是否有最佳实践或普遍商定的一组选项来缓解此问题?(我编了学生/班级的例子,不要纠结于具体细节。:))

我们能想到什么

  • 简单地告诉这些客户端的开发人员:从现在开始,你必须为你调用的每个端点实现重试机制。缺点似乎很明显。
  • 实现一个等待 B 准备就绪的API 网关。缺点:有许多可以想象的工作流程(涉及更多模块 AZ)需要这样做,因此网关可能会变得相当复杂。
  • 以某种方式为客户端创建一个“会话”来跟踪它连续发出的请求。然后 B 可以确定是否应该等待来自 A 的消息,或者甚至可以通过查看客户端向 A 发出的精确请求来更新其状态。

有更好的方法吗?哪个最合适?

编辑:澄清该问题主要涉及以自动方式调用端点的第三方客户端的行为,这意味着即使最终一致性中的几毫秒“滞后”也可能是致命的。

Cos*_*iță 2

该问题的以强一致性为中心的解决方案基于分布式事务,不幸的是,分布式事务具有很高的复杂性和性能影响。

这篇关于整体架构到微服务迁移的精彩文章中,Zhamak Dehghani 也解决了数据不一致的问题:

众所周知,分布式事务很难实现,因此微服务架构强调服务之间的无事务协调,并明确认识到一致性可能只是最终的一致性,通过补偿操作来解决问题

因此,最终一致性是基于微服务的架构中唯一的数据一致性选项,如果您需要强一致性保证,那么您需要构建变通办法(补偿操作),例如重试流程,这会增加额外的复杂性。

此外,本文还强调了一种非常有见地的方式来查看业务工作流程中的数据不一致:

选择以这种方式管理不一致对于许多开发团队来说是一项新挑战,但它通常符合业务实践。通常,企业会处理一定程度的不一致,以便快速响应需求,同时采用某种逆转流程来处理错误。只要在更高的一致性下修复错误的成本低于失去业务的成本,这种权衡就是值得的

这是我看待这个问题的方式:

  • 确实,微服务 A 和 B 之间的存储以异步方式更新,但此更新工作流程的确切延迟是多少?如果我们谈论的是 1 - 2 秒,那么用户可能会完全感觉到不一致。否则,系统应该进行扩展以支持这个(甚至更低)的延迟阈值。
  • 您可以监视不一致事件 - 当用户尝试获取存储中不存在的数据(因为它处于更新过程中)时,并基于此扩展您的系统。
  • 最重要的是,它可能有助于衡量这种一致性保证的需要,然后应用合适的解决方法。