假设我们有一个用户、钱包REST微服务和一个API网关,它将这些东西粘合在一起。当Bob在我们的网站上注册时,我们的API网关需要通过user微服务创建一个用户,并通过wallet微服务创建一个钱包。

下面是一些可能出错的情况:

用户Bob创建失败:没关系,我们只是向Bob返回一个错误消息。我们使用SQL事务,所以没有人在系统中看到Bob。一切都很好:) 用户Bob已经创建,但是在我们的钱包创建之前,我们的API网关硬崩溃了。我们现在有一个没有钱包的用户(数据不一致)。 创建了用户Bob,当我们创建钱包时,HTTP连接断开。钱包的创建可能成功,也可能失败。

有什么解决方案可以防止这种数据不一致的发生?是否存在允许事务跨越多个REST请求的模式?我读过维基百科上关于两阶段提交的页面,它似乎涉及到这个问题,但我不确定如何在实践中应用它。这篇原子分布式事务:一篇RESTful设计论文看起来也很有趣,尽管我还没有读过。

或者,我知道REST可能不适合这个用例。处理这种情况的正确方法可能是完全放弃REST并使用不同的通信协议,如消息队列系统?或者我应该在我的应用程序代码中强制一致性(例如,通过有一个后台作业来检测不一致并修复它们,或者通过在我的用户模型上有一个“state”属性与“creating”,“created”值等)?


当前回答

为什么不使用支持脚本/编程的API管理(APIM)平台?因此,您将能够在APIM中构建复合服务,而不会干扰微服务。为此,我使用APIGEE进行设计。

其他回答

一个简单的解决方案是使用用户服务创建用户,并使用消息总线,其中用户服务发出其事件,钱包服务在消息总线上注册,监听用户创建的事件并为用户创建钱包。同时,如果用户进入钱包UI查看他的钱包,检查用户是否刚刚创建,并显示您的钱包正在创建中,请过一段时间再检查

所有分布式系统都存在事务一致性问题。最好的方法是像你说的那样,进行两阶段提交。在挂起状态下创建钱包和用户。创建该用户后,进行单独调用以激活该用户。

最后一个调用应该是安全可重复的(以防连接断开)。

这将需要最后一次调用知道两个表(这样就可以在单个JDBC事务中完成)。

或者,您可能想要考虑一下为什么您如此担心没有钱包的用户。你认为这会引起问题吗?如果是这样,也许将它们作为单独的rest调用是一个坏主意。如果用户没有钱包就不应该存在,那么您可能应该将钱包添加到用户中(在最初的POST调用中创建用户)。

为什么不使用支持脚本/编程的API管理(APIM)平台?因此,您将能够在APIM中构建复合服务,而不会干扰微服务。为此,我使用APIGEE进行设计。

如果我的钱包只是用户在同一个sql数据库中的另一组记录,那么我可能会将用户和钱包的创建代码放在同一个服务中,并使用正常的数据库事务设施进行处理。

在我看来,你是在问,当钱包创建代码要求你接触另一个或多个系统时会发生什么?我认为这完全取决于创建过程的复杂性和风险。

如果只是涉及到另一个可靠的数据存储(比如一个不能参与sql事务的数据存储),那么根据整个系统参数,我可能愿意冒第二次写入不会发生的小概率风险。我可能什么也不做,只是引发一个异常,并通过补偿事务或甚至一些特别方法处理不一致的数据。就像我总是告诉开发者的那样:“如果这类事情发生在应用中,它不会被忽视。”

随着钱包创建的复杂性和风险的增加,您必须采取措施来改善所涉及的风险。假设有些步骤需要调用多个合作伙伴api。

此时,您可能会引入消息队列以及部分构造的用户和/或钱包的概念。

确保实体最终被正确构造的一个简单而有效的策略是让作业重试,直到它们成功为止,但这在很大程度上取决于应用程序的用例。

我也会仔细思考为什么我的配置过程中有一个容易失败的步骤。

恕我直言,微服务架构的一个关键方面是事务被限制在单个微服务中(单一责任原则)。

在当前示例中,User创建将是一个自己的事务。用户创建将把USER_CREATED事件推入事件队列。钱包服务将订阅USER_CREATED事件并创建钱包。