随着互联网科技的进步,使我们的生活更加便捷,为了能够提供长期稳定的服务,分布式服务架构逐渐成为主流。那么与之伴随而来的是分布式事务。
我们日常生活中哪些场景会用到分布式事务呢?举个简单的例子,当我们用淘宝购物,下单成功时,那么对应商品的库存应该同时-1。再比如,我们用支付宝向A同学转账1w,那么需要在我的账户扣减1w,同时在James同学的余额账户增加1w,诸如此类的问题,我们在很多地方都能找到类似的场景。
当非分布式服务的场景中,我们可以依靠本地事务,来保证事务的完整性。那么分布式服务的场景中,我们要保证的是 消息的最终一致性。
@Transactional(rollbackFor=Exception.class)
public void update() {
// 扣减我账户余额
boolean updSuccess = updateMyAccount();
if(!updSuccess){// 余额不足,流程终止
return;
}
// 扣减成功,更新James同学账户
updateJamesAccount();
}
同样的问题在分布式场景中,我们如何做呢?考虑到效率的问题,我们引入了新的概念,即:
大事务 = 小事务 + 异步
上述的场景拆分成
扣减我的余额
@Transactional(rollbackFor=Exception.class)
public void update() {
// 扣减我账户余额
boolean updSuccess = updateMyAccount();
if(!updSuccess){// 余额不足,流程终止
return;
}
sendMqMsg();// 发送mq消息
}
增加james同学账户余额
// 扣减成功,更新James同学账户
updateJamesAccount();
此时我们发现扣减我的余额后 我们可以通过MQ异步通知增加james同学账户余额,这样就做到小事务+异步了吗,此时还有什么其他的问题吗?

上述可能出现的场景中,我们发现问题的本质是 扣减账户余额的业务逻辑和发送mq消息之间无法保障操作的原子性。
事务消息
为了解决具体的业务逻辑以及推送mq之间的原子性,事务消息诞生了。那么事务消息如何解决上述问题呢?

此时,我们解决了具体的业务逻辑以及推送MQ之间的原子性,即producer和MQ的原子性。
常见的问题
-
问:如果预发送消息失败,是不是业务就不执行了?
答:是的,对于基于消息最终一致性的方案,一般都会强依赖这步,如果这个步骤无法得到保证,那么最终也 就不可能做到最终一致性了。
-
问:为什么要增加一个消息
预发送机制,增加两次发布出去消息的重试机制,为什么不在业务成功之后,发送失败的话使用一次重试机制?答:如果业务执行成功,再去发消息,此时如果还没来得及发消息,业务系统就已经宕机了,系统重启后,根本没有记录之前是否发送过消息,这样就会导致业务执行成功,消息最终没发出去的情况。
-
如果consumer消费失败,是否需要producer做回滚呢?
答:这里的事务消息,producer不会因为consumer消费失败而做回滚,采用事务消息的应用,其所追求的是高可用和最终一致性,消息消费失败的话,MQ自己会负责重推消息,直到消费成功。因此,事务消息是针对生产端而言的,而消费端,消费端的一致性是通过MQ的重试机制来完成的。
-
如果consumer端因为业务异常而导致回滚,那么岂不是两边最终无法保证一致性?
答:基于消息的最终一致性方案必须保证消费端在业务上的操作没障碍,它只允许系统异常的失败,不允许业务上的失败,比如在你业务上抛出个NPE之类的问题,导致你消费端执行事务失败,那就很难做到一致了。
参考文章链接:https://www.jianshu.com/p/eb571e4065ec,作者:CoderZS