讲讲消息队列的事务消息

来自:孤独烟(微信号:zrj_guduyan),作者:孤独烟

引言

本文是《异步服务调用利器(消息队列)》的续集。当然,烟哥会一如既往的保持通俗易懂的文风,循序渐进,力求把知识点讲明白!
另外,我会先回忆一下前篇的思路,然后回答一下大家留言问我最多的问题!接着引出事务消息的概念,讲讲事务消息的应用!

正文

前情回顾

我们先回忆一下《异步服务调用利器(消息队列)》这篇文章所讲的,利用消息队列解决分布式事务的主要内容。
场景:支付宝给余额宝转账100元
生产者:
事务开始
    给支付宝账户zhangsan,扣100元
    给事件表插入一条记录
事务结束

另外,起一个定时程序,定时扫描事务表,发送消息!

消费者:
事务开始
    将消息插入消息表
    给余额宝账户zhangsan,加100元
事务结束

疑难解答

消费者将消息插入消息表失败了怎么办?
OK,这个问题可以这么理解,消费者执行事务失败了怎么办?很简单啊,现在消费者都可以设置手动应答模式,不要设置自动应答模式。等事务执行成功,再给消息队列发应答消息,消息队列就知道该消息被消费过了。如果事务执行失败,不要给消息队列发应答消息,就让消息在消息队列中堆积!

消费者消费失败了,怎么补偿?
有什么好补偿的,直接人工干预!一般来说,消费者在消费失败的情况下,要么是程序逻辑出问题,要么是机器出问题!你难道指望靠补偿进行恢复?不可能的!

不进行补偿,就让消息一直堆积么?
是的,就让一直消息堆!以RocketMq为例,至少能堆积上亿级别的消息。你们哪个业务,一天会产生上亿级别的消息?一般一天几千万最多了。正常情况下,当天就能发现问题,马上进行人工干预处理!不丢消息,才是最重要的!

方案缺点

《异步服务调用利器(消息队列)》出的方案,有一个缺点没有指明。

  • 缺点:平白无故多了一个定时轮询程序,与业务无关!

事务消息

我这里引用一下,RocketMq官网的内容

Transactional message ensures that the execution of local transaction and the sending of message can be performed atomically.

说的通俗点,就是能够保证执行本地事务和发送消息,这两个操作是一个原子性操作!
再通俗一点就是,使用事务消息,能够保证下面的操作
事务开始
    (1)给支付宝账户zhangsan,扣100元
    (2)给消息队列发送一条数据
事务结束
这个操作是原子性操作,要么一起成功,要么一起失败!

在这里需要说明一下,使用事务消息的功能前,你一定要确认你的消息队列有没有提供这个功能!并非所有的消息队列都提供了事务消息的功能!

原理

我这里介绍一下,RocketMq中事务消息的原理,其他的消息队列,大家可自行查询!
其实原理很简单,就是把消息的发送分成了2个阶段:Prepare阶段Commit阶
具体来说,上面的2个步骤,被分解成3个步骤: 
事务开始
    (1)发送Prepared消息 
    (2)给支付宝账户zhangsan,扣100元
    (3)根据update DB结果成功或失败,Confirm或者rollback消息
事务结束

唯一的问题
第(1),(2)步执行成功了,第(3)步失败了怎么办?这里就涉及到了RocketMq的关键点:事务回查机制!RocketMq会定期扫描所有的Prepared消息,询问发送方,到底是要确认这条消息发出去?还是取消此条消息?

一般来说,若生产者执行本地事务超过6s则进行第一次事务回查,总共回查15次,后续回查间隔时间是60s,这些都是可以配置的!
时序图
为了方便大家理解,我画了一张时序图,方便大家理解


优点
生产者不用做“扫描消息表”的操作,交给消息队列回查!


代码

这里我直接贴一个官网的事务消息的demo地址,大家有兴趣可以自行观看:
http://rocketmq.apache.org/docs/transaction-example/

总结

本文介绍了消息队列中的事务消息,希望大家有所收获!

推荐↓↓↓
Java编程
上一篇:谈谈线上CPU100%排查套路 下一篇:JSP面试题都在这里