假设我们有一个用户、钱包REST微服务和一个API网关,它将这些东西粘合在一起。当Bob在我们的网站上注册时,我们的API网关需要通过user微服务创建一个用户,并通过wallet微服务创建一个钱包。
下面是一些可能出错的情况:
用户Bob创建失败:没关系,我们只是向Bob返回一个错误消息。我们使用SQL事务,所以没有人在系统中看到Bob。一切都很好:)
用户Bob已经创建,但是在我们的钱包创建之前,我们的API网关硬崩溃了。我们现在有一个没有钱包的用户(数据不一致)。
创建了用户Bob,当我们创建钱包时,HTTP连接断开。钱包的创建可能成功,也可能失败。
有什么解决方案可以防止这种数据不一致的发生?是否存在允许事务跨越多个REST请求的模式?我读过维基百科上关于两阶段提交的页面,它似乎涉及到这个问题,但我不确定如何在实践中应用它。这篇原子分布式事务:一篇RESTful设计论文看起来也很有趣,尽管我还没有读过。
或者,我知道REST可能不适合这个用例。处理这种情况的正确方法可能是完全放弃REST并使用不同的通信协议,如消息队列系统?或者我应该在我的应用程序代码中强制一致性(例如,通过有一个后台作业来检测不一致并修复它们,或者通过在我的用户模型上有一个“state”属性与“creating”,“created”值等)?
如果我的钱包只是用户在同一个sql数据库中的另一组记录,那么我可能会将用户和钱包的创建代码放在同一个服务中,并使用正常的数据库事务设施进行处理。
在我看来,你是在问,当钱包创建代码要求你接触另一个或多个系统时会发生什么?我认为这完全取决于创建过程的复杂性和风险。
如果只是涉及到另一个可靠的数据存储(比如一个不能参与sql事务的数据存储),那么根据整个系统参数,我可能愿意冒第二次写入不会发生的小概率风险。我可能什么也不做,只是引发一个异常,并通过补偿事务或甚至一些特别方法处理不一致的数据。就像我总是告诉开发者的那样:“如果这类事情发生在应用中,它不会被忽视。”
随着钱包创建的复杂性和风险的增加,您必须采取措施来改善所涉及的风险。假设有些步骤需要调用多个合作伙伴api。
此时,您可能会引入消息队列以及部分构造的用户和/或钱包的概念。
确保实体最终被正确构造的一个简单而有效的策略是让作业重试,直到它们成功为止,但这在很大程度上取决于应用程序的用例。
我也会仔细思考为什么我的配置过程中有一个容易失败的步骤。
最终的一致性是这里的关键。
选择其中一个服务作为事件的主要处理程序。
该服务将处理单个提交的原始事件。
主处理程序将负责将次要效果异步地传递给其他服务。
主处理程序将对其他服务调用进行编排。
指挥官负责分布式事务并进行控制。它知道要执行的指令,并协调执行它们。在大多数情况下,只有两条指令,但它可以处理多条指令。
指挥官负责保证所有指令的执行,这意味着退休。
当指挥官试图执行远程更新而没有得到响应时,它没有重试。
通过这种方式,系统可以配置为更不容易出现故障,并自我修复。
当我们进行重试时,我们有幂等性。
幂等性是这样一种性质,即可以做某事两次,最终结果与只做一次一样。
我们需要远程服务或数据源上的幂等性,以便在它多次接收指令的情况下,它只处理一次指令。
最终一致性
这解决了大多数分布式事务的挑战,但是我们需要考虑以下几点。
每一个失败的事务都将被重试,尝试重试的数量取决于上下文。
一致性是最终的,即在重试期间系统处于不一致的状态,例如,如果客户订购了一本书,并支付了费用,然后更新了库存数量。如果库存更新操作失败,并且假设这是最后一个可用的库存,那么在库存更新的重试操作成功之前,这本书仍然可用。重试成功后,您的系统将保持一致。
在微服务世界中,服务之间的通信应该通过rest客户端或消息队列。有两种方法可以跨服务处理事务,具体取决于服务之间的通信方式。我个人更喜欢消息驱动的体系结构,这样长时间的事务对用户来说应该是一个无阻塞的操作。
让我们举个例子来解释一下:
使用事件Create user创建用户BOB,并将消息推送到消息总线。
订阅此事件的钱包服务可以创建用户对应的钱包。
The one thing which you have to take care is to select a robust reliable message backbone which can persists the state in case of failure. You can use kafka or rabbitmq for messaging backbone. There will be a delay in execution because of eventual consistency but that can be easily updated through socket notification. A notifications service/task manager framework can be a service which update the state of the transactions through asynchronous mechanism like sockets and can help UI to update show the proper progress.