可靠事件队列

x33g5p2x  于2022-03-13 转载在 其他  
字(1.3k)|赞(0)|评价(0)|浏览(388)

一 BASE 理论

BASE 分别是基本可用性(Basically Available)、柔性事务(Soft State)和最终一致性(Eventually Consistent)的缩写。

二 案例

以交易过程中正确修改账号、仓库和商家服务中的数据为例进行说明。

1 最终用户向书店发送交易请求,购买一本价值为 100 元的书。

2 系统首先应对用户账号扣款、商家账号收款、库存商品出库这三个操作做出一个出错率的先验评估,根据出错概率的大小来安排它们的操作顺序,这种评估一般直接体现在程序代码中,一些大型系统也可能会实现动态排序。譬如,根据统计,最有可能出现交易异常的用户购买了商品,但是不同意扣款,或者账号余额不足;其次是仓库发现商品库存不够,无法发货;风险最低的是收款,如果到了商家收款环节,一般就不会出现意外了。那最容易出错的就应该最先进行,即:账号扣款 > 仓库出库 > 商家收款。

3 账户服务进行扣款业务,如果扣款成功,则在自己的数据库建立一张消息表,里面存入一条消息:“事务ID:某UUID,扣款100元(状态:已完成),仓库出库某某书籍:1本(状态:进行中),某商家收款:100元(状态:进行中)”。注意,这个步骤中“扣款业务”和“写入消息”是使用同一个本地事务写入账号服务自己的数据库的。

4 在系统建立一个消息服务,定时轮询消息表,将状态是“进行中”的消息同时发送到库存和商家服务节点中去。这时候可能产生以下几种情况。

a 商家和仓库服务都成功完成了收款和出库工作,向用户账号服务器返回执行结果,用户账号服务把消息状态从“进行中”更新为“已完成”。整个事务顺利结束,达到最终一致的状态。

b 商家或仓库服务中至少有一个因为网络原因,未能收到来自用户账号服务的消息。此时,由于账号服务器中存储的消息一直处于“进行中”,所以消息服务器将在每次轮询的时候持续向未响应的服务重复发送消息。这个步骤的可重复性决定了所有消息被消息服务器发送的消息必须具备幂等性,通常的设计是让消息带上一个唯一的事务 ID,以保障一个事务中的出库、收款动作会且只会被处理一次。

c 商家或仓库服务中有某个或全部无法完成工作,例如,发现书没有库存了,此时,仍然是持续自动发送消息,直到操作成功(例如补充了新书),或者被人工介入为止。由此可见,可靠事件队列只要第一步业务完成了,后续就没有失败回滚的概念,只许成功,不许失败。

d 商家或仓库服务成功完成了收款或出库工作,但回复的应答消息因网络原因丢失了,此时,用户账号服务仍然会重新发出下一条消息,但因操作具备幂等性,所以不会导致重复出库和收款,只会导致商家、仓库服务器重新发送一条应答消息,此过程持续自动重复直到双方网络通信恢复正常。

e 也有一些支持分布式事务的消息框架,如 RabitMQ,原生就支持分布式事务操作,这时候上述的第二和第四种情况也可以交由消息框架来保障。

这是一种“最大努力交付”的设计理念。而可靠时间队列还有一种更普遍的形式,被称为“最大努力一次提交”,指的是将最有可能出错的业务以本地事务方式完成后,采用不断重试的方式(不限于消息系统)来促使同一个分布式事务中的其他关联业务全部完成。

相关文章