在基于微服务的系统中实现数据库一致性的最佳方法是什么?
在GOTO in Berlin,Martin Fowler谈论微服务,他提到的一条“规则”是保留“每服务”数据库,这意味着服务不能直接连接到另一个服务“拥有”的数据库
这是超级优雅和优雅,但在实践中它变得有点棘手。假设您有一些服务:
现在,客户在您的前端进行购买,这将调用订单管理服务,这将保存数据库中的所有内容 - 没问题。此时,还会拨打忠诚度计划服务,以便从您的帐户中记入/记入积分。
现在,当所有内容都在同一个DB / DB服务器上时,一切都变得简单,因为您可以在一个事务中运行所有内容:如果忠诚度计划服务无法写入数据库,我们可以回滚整个事情。
当我们在多个服务中执行数据库操作时,这是不可能的,因为我们不依赖于一个连接/利用运行单个事务。 什么是保持事物一致并过上幸福生活的最佳模式?
我非常渴望听到您的建议!并提前致谢!
答案 0 :(得分:11)
这是超级优雅和优雅,但在实践中它变得有点棘手
“在实践中”的含义是,您需要设计您的微服务,以便在遵循规则时满足必要的业务一致性:
服务不能直接连接到另一个服务“拥有”的数据库。
换句话说 - 不要对他们的责任作出任何假设,并根据需要改变界限,直到找到一种方法使其发挥作用。
现在,问你的问题:
保持事物一致并过上幸福生活的最佳模式是什么?
对于不需要立即一致性的事情,并且更新忠诚度积分似乎属于该类别,您可以使用可靠的发布/订阅模式从一个微服务调度事件以供其他人处理。可靠的一点是,您需要为事件处理提供良好的重试,回滚和幂等性(或事务性)。
如果您在.NET上运行,那么支持此类可靠性的基础架构示例包括NServiceBus和MassTransit。完全披露 - 我是NServiceBus的创始人。
更新:关于忠诚度积分问题的评论:“如果延迟处理余额更新,客户实际上可以订购的商品数量多于他们的积分”。
许多人为了强烈的一致性而努力满足这些要求。问题在于,通常可以通过引入其他规则来处理这些情况,例如,如果用户最终通过负忠诚点通知他们。如果T在没有忠诚点被分类的情况下经过,则通知用户他们将根据某些转换率向他们收费M.客户在使用积分购买东西时应该可以看到此政策。
答案 1 :(得分:7)
我通常不会处理微服务,这可能不是一种好的做事方式,但这是一个想法:
为了重申这个问题,该系统由三个独立但相互沟通的部分组成:前端,订单管理后端和忠诚度计划后端。前端希望确保在订单管理后端和忠诚度计划后端中保存一些状态。
一种可能的解决方案是实现某种类型的two-phase commit:
如果实现了这一点,那么更改不一定是 atomic ,但它将最终一致。让我们想想它可能失败的地方:
答案 2 :(得分:0)
我同意@Udi Dahan所说的话。只是想补充一下他的答案。
我认为您需要将请求保留到忠诚度计划中,这样如果失败,可以在其他方面完成。有多种方法可以说/做到这一点。
1)使忠诚度计划API失败可恢复。也就是说它可以持久保存请求,以便它们不会丢失,并且可以在稍后的某个时间恢复(重新执行)。
2)异步执行忠诚度计划请求。也就是说,首先在某处保留请求,然后允许服务从此持久存储中读取它。仅在成功执行时从持久存储中删除。
3)做Udi所说的,把它放在一个好的队列上(确切地说是pub / sub模式)。这通常要求订户执行以下两种操作之一...要么在从队列中删除之前保留请求(转到1) - 或者 - 首先从队列中借用请求,然后在成功处理请求之后,请求请求从队列中删除(这是我的偏好)。
这三个人完成了同样的事情。他们将请求移到一个持久的地方,在那里可以继续工作直到成功完成。请求永远不会丢失,必要时重试,直到达到满意的状态。
我喜欢用接力赛的例子。在允许前一段代码放弃之前,每个服务或代码段必须保留并拥有该请求。一旦它被移交,当前所有者不得丢失请求,直到它被处理或移交给其他一些代码。
答案 3 :(得分:0)
即使对于分布式事务,如果其中一个参与者在事务中崩溃,您也可以进入“处于疑问状态”。如果您将服务设计为幂等操作,那么生活就会变得容易一些。人们可以编写程序来满足没有XA的业务条件。帕特·赫兰德(Pat Helland)撰写了一篇名为“Life Beyond XA”的优秀论文。基本上,该方法是尽可能地做出关于远程实体的最小假设。他还阐述了一种称为开放嵌套事务(http://www.cidrdb.org/cidr2013/Papers/CIDR13_Paper142.pdf)的方法来建模业务流程。在这种特定情况下,购买交易将是顶级流程,忠诚度和订单管理将是下一级流程。诀窍是将粒度服务作为具有补偿逻辑的幂等服务。因此,如果任何事情在流程中的任何地方失败,个别服务可以补偿它。所以例如如果订单因某种原因失败,忠诚度可以扣除该购买的累计积分。
其他方法是使用CALM或CRDT使用最终一致性进行建模。我写了一篇博客来强调在现实生活中使用CALM - http://shripad-agashe.github.io/2015/08/Art-Of-Disorderly-Programming可能会帮助你。