MQ事务消息原理解析

随着互联网科技的进步,使我们的生活更加便捷,为了能够提供长期稳定的服务,分布式服务架构逐渐成为主流。那么与之伴随而来的是分布式事务

我们日常生活中哪些场景会用到分布式事务呢?举个简单的例子,当我们用淘宝购物,下单成功时,那么对应商品的库存应该同时-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同学账户余额,这样就做到小事务+异步了吗,此时还有什么其他的问题吗?

image.png

上述可能出现的场景中,我们发现问题的本质是 扣减账户余额的业务逻辑和发送mq消息之间无法保障操作的原子性。

事务消息

为了解决具体的业务逻辑以及推送mq之间的原子性,事务消息诞生了。那么事务消息如何解决上述问题呢?


MQ事务消息流程.jpg

此时,我们解决了具体的业务逻辑以及推送MQ之间的原子性,即producer和MQ的原子性。

常见的问题

  1. 问:如果预发送消息失败,是不是业务就不执行了?

    答:是的,对于基于消息最终一致性的方案,一般都会强依赖这步,如果这个步骤无法得到保证,那么最终也 就不可能做到最终一致性了。

  2. 问:为什么要增加一个消息预发送机制,增加两次发布出去消息的重试机制,为什么不在业务成功之后,发送失败的话使用一次重试机制?

    答:如果业务执行成功,再去发消息,此时如果还没来得及发消息,业务系统就已经宕机了,系统重启后,根本没有记录之前是否发送过消息,这样就会导致业务执行成功,消息最终没发出去的情况。

  3. 如果consumer消费失败,是否需要producer做回滚呢?

    答:这里的事务消息,producer不会因为consumer消费失败而做回滚,采用事务消息的应用,其所追求的是高可用最终一致性,消息消费失败的话,MQ自己会负责重推消息,直到消费成功。因此,事务消息是针对生产端而言的,而消费端,消费端的一致性是通过MQ的重试机制来完成的。

  4. 如果consumer端因为业务异常而导致回滚,那么岂不是两边最终无法保证一致性?

    答:基于消息的最终一致性方案必须保证消费端在业务上的操作没障碍,它只允许系统异常的失败,不允许业务上的失败,比如在你业务上抛出个NPE之类的问题,导致你消费端执行事务失败,那就很难做到一致了。

参考文章链接:https://www.jianshu.com/p/eb571e4065ec,作者:CoderZS

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容