一文搞懂RocketMQ事务

720 阅读4分钟

「这是我参与2022首次更文挑战的第29天,活动详情查看:2022首次更文挑战

hello,大家好,我是L_Denny,是的,我又来了,带来了我首次更文挑战的第28篇,马上到达终点了,让我们开始吧!

目前市场上,大多数公司都会使用消息中间件,比较热门的就是RocketMQ或者Kafka吧。其中RocketMQ有个比较重要的特性,就是RocketMQ是自带了一个事务,可以保证消息投递的不丢失。一般面试时,涉及到关于RocketMQ,都会问到这样一个问题:RocketMQ 的事务消息如何来保证数据的最终一致性?,下面就揭开谜底

RocketMQ事务消息的实现流程

image.png

  1. 上游服务会先发送一个 half 消息给 RocketMQ
  2. half 消息发送成功,RocketMQ 收到这个 half 消息后,会返回一个 success 响应给上游服务
  3. 上游服务接收到 RocketMQ 返回的 success 响应后,开始处理本地的业务逻辑,并提交本地事务。
  4. 如果上游服务本地事务提交成功,则会向RocketMQ中发送 commit,表示将 half 消息提交
  5. 如果上游服务本地事务提交失败,则直接回滚本地事务,并向RocketMQ中发送 rollback,表示将之前的 half 消息进行回滚,MQ 接收到 rollback 消息后,就会将 half 消息删除。
  6. 如果RocketMQ收到commit,就将消息写入磁盘
  7. 如果RocketMQ收到half,就丢弃消息
  8. 如果 RocketMQ 长时间没有接收到 commit 或者 rollback 消息,就会在一定时间后尝试调用上游服务提供的一个接口,通过这个接口来判断 half 消息的状态。

为什么要发送个half消息?有什么用?
这个half消息是在消息正式发送之前提前发送的,对下游的消费者不可见,更多的作用是下探rocketMQ是否正常。如果无法正常通信,则上游服务可以直接返回一个异常,也就不用处理后面的逻辑的了。

half是什么?
half其实也是一个消息,和我们发送的消息一样,都是存储在 RocketMQ 中,唯一不同的是 half 在 RocketMQ 中不会立马被消费者消费到,除非这个 half 消息被 commit 了。

在没有commit之前,RocketMQ对于事务消息而言,会先放在一个内部队列中,只有 commit 了,才会真正将消息放在消费者能读取到的 topic 队列中。

half消息如果写入失败了怎么办?
如果half消息写入失败,就可以认为MQ的服务是有问题的,这时,就不能通知下游服务了,我们可以给这个消息一个状态标记,然后等待MQ服务正常后再进行补偿操作,等MQ服务正常后重新下单通知下游服务。

其实整体来说,在业务场景下,消息不丢失的问题实际上是上游服务的业务与下游服务的业务的分布式事务一致性问题。而事务一致性问题一直以来都是一个非常复杂的问题。

而RocketMQ的事务消息机制,实际上只保证了整个事务消息的一半,保证的是上游服务业务的逻辑处理和发消息这两个事件的事务一致性,而对下游服务的事务并没有保证。但是即便如此,也是分布式事务的一个很好的降级方案。

目前来说,保证数据的最终一致性,是比较多公司采用的方案,一般都是通过RocketMQ的可靠消息和补偿机制来保证的,毕竟分布式事务的成本还是比较高的。

实际使用

发送 half 消息给 RocketMQ,主要通过TransactionMQProducer类来实现

TransactionMQProducer transactionMQProducer = new TransactionMQProducer("producerGroup");
TransactionSendResult result = transactionMQProducer.sendMessageInTransaction(msg, null);
// 通过result来判断half消息是否发送成功
if(result.getSendStatus() == SendStatus.SEND_OK){
    // 成功
}else{
    // 失败
}

实现监听接口TransactionListener,提供给到RocketMQ回调

public interface TransactionListener { 
    // 当half消息发送成功后,在这里实现业务逻辑,然后commit或者rollback 
    LocalTransactionState executeLocalTransaction(final Message msg, final Object arg);
    
    // 这个方法就是提供给RocketMQ回调的方法,RocketMQ通过回调该方法来判断half消息的状态 
    // 这个方法的参数是MessageExt,就是half消息的内容,如果根据MessageExt,可以判断之间的业务逻辑是否处理成功
    LocalTransactionState checkLocalTransaction(final MessageExt msg); 
}

总结

RocketMQ是面试一些大厂必问的中间件了,这里只介绍了它的事务特性,但是RocketMQ的知识点远远不止这边,最近阿里发布了RocketMQ的新的版本,有兴趣的同学可以去看看它的新特性,这样有机会也可以和别人吹吹RocketMQ对吧。