思路分析
首先描述下我这边的场景,也是很常见的一个异步调用场景:
即将服务A假设为某电商用户模块,服务B假设为电商活动模块。
我这边呢,假设用户支付多少钱,就返多少钱的一个代金券。

一致性解决
梳理一下流程,上面这一版有一个致命的问题!如下所示
事务开始
(1)给alili账户扣10元
(2)给alili账户发10元代金券封装为消息,发送给消息队列
事务结束
那么来了,如何保证第一步和第二步是在同一个事务里完成的。换句话说,第一步操作的是数据库,第二步操作的是一个消息队列,你如何保证这两步之间的一致性?
记住了,任何涉及到数据库和中间件之间的业务逻辑操作,都需要考虑二者之间的一致性。比如,你先操作了数据库,再操作缓存,数据库和缓存之间一致性如何解决?
改变思路,加一张事务表,如下图所示

注意了,此时事务的内容为
事务开始
(1)给账户alili,扣10元
(2)给事件表插入一条记录
事务结束
此时是对同一数据库的两张表操作,因此可以用数据库的事务进行保证。
另外,起一个定时程序,定时扫描事务表,发现一个状态为'UNFINISHED'的事件,就进行封装为消息,发送到消息中间件,然后将状态改为'FINISHED'.
幂等性解决
注意了,这一版还存在一个幂等性问题!
仔细看,定时程序做了如下三个操作
(1)定时扫描事务表,发现一个状态为'UNFINISHED'的事件
(2)将事件信息,封装为消息,发送到消息中间件
(3)将事件状态改为'FINISHED'
假设在步骤(2)的时候,发送完消息体,还未执行步骤(3),定时程序阵亡了!然后重启定时程序,发现刚那个事务的状态依然为'UNFINISHED',因此重新发送。这样,就会出现重复消费问题。因此,幂等性也是需要保证的!

给代金券表添加事务id字段,如果一旦出现重复消费,则在事务里直接报出唯一约束冲突错误,从而保证了幂等性!

是不是觉得到这里就完了?hiahia,
消费者确定消费
仔细想,我这边消息队列是给消费者消息了,然后是不是就这个消息会在队列中清除了,
是的,这就是 消息队列 默认的自动ack机制,ack简单说就是个确认信息,
也就是说我消费者拿到这条信息就告诉队列我已经拿到了,你可以删除了,那这边又出现了一个问题,消费者怎么能确定自己手上这条信息在流程中不会出问题呢,按道理我们是要消费者做完事情在告诉队列去删除,我出问题了你下次再给我重发我再次消费,所以这里我们要开启手动ack在执行完业务逻辑后手动提交,以此来保证整个流程的数据一致性。

