原理:执行本地事务之前,先发送一个消息到mq中(该消息只存储在commitlog中,在consumeQueue中不可见,消费端无法看到此消息)。在本地事务执行完毕后,再到达确认(commit)阶段,该阶段主要把半事务消息保存到consumeQueue中,消费者可以看到此消息正常消费,反之是rollback就不保存。
如本地事务执行时间较长(异步执行),在确认阶段无法及时得到本地事务执行成功的消息。则需要自定义定时执行事务回查机制(默认60s执行一次),如回查到本地事务执行完成的消息后则进行消息正式提交,或进行半事务的消息回滚。
半事务阶段:该阶段主要发一个消息到rocketmq,但此消息只存储在commitlog中,但consumeQueue中不可见,也就是消费端无法看到此消息
确认阶段(commit/rollback):该阶段主要是把半事务消息保存到consumeQueue中,即让消费端可以看到此消息可正常消息,如果是rollback就不保存
事务回查:默认每60s执行回查半事务消息一次,如发现本地事务业务已经成功了,就补发"发送commit确认消息" 复杂业务中可设计一张Transaction表,将业务表和Transaction绑定在同一个本地事务中,如本地事务执行成功时则记录Transaction表的中状态为"已完成",当mq事务回查时,只需检查对应事务的状态是否是"已完成"即可,而无需关心具体的业务数据。
注意点:rockeqmq(几乎所有的消息中间件)不能保障消息的重复(网络波动等原因导致消息被消费了,但offset未提交到mq中),所以在消费端一定要做幂等性处理。
如果消费端发生消费失败,同时也需要做重试,如果重试多次,消息会进入死信队列,这个时候也需要进行特殊处理。(一般把原业务的执行进行回退)
使用限制:
1:事务消息不支持延时消息和批量消息
2:事务回查的间隔时间:BrokerConfig.transactionCheckInterval 通过Broker的配置文件设置
3:为了避免单个消息被检查太多次而导致半对对列消息累积,默认将单个消息的检查次数限制为15次,可通过broker配置文件的transactionCheckMax参数来修改次限制。如已经检查某条消息超过N次(N=transactionCheckMax)则broker将丢弃此消息,同时打印错误日志。开发可通过重写AbstractTransactionCheckListener类修改这个行为
4:事务消息在配置文件中的参数transactionMsgTimeout的特定时间长度之后被检查,发送事务消息时,也可通过设置用户属性CHECK_IMMUNITY_TIME_IN_SECONDS来改变这个限制,优于transactionMsgTimeout
5:事务性消息可能不止一次被检查或消费
6:事务性消息中用到了生产者群组,是一种高可用机制,用来确保事务消息的可靠性
7:提交给用户的目标主题消息可能会失败,目前依日志的记录而定,高可用性通过RocketMQ本身的高可用性机制来保证,如希望确保事务消息不丢失,并且事务完整性得到保证,建议使用同步的双重写入机制
8:事务消息的生产者ID不能与其他类型消息的生产者ID共享。与其他类型的消息不同,事务消息允许反向查询,MQ服务器能通过生产者ID查询到消费者