RocketMQ事务性消息
mq事务介绍
mq事务消息流程
- 生产者发送消息到mq,消息状态为:SEND_OK。此消息是消费者不可见(消费者无法消费此条消息)
- 执行本地任务:成功则返回COMMIT_MESSAGE,此时消费者可消费此条消息。失败则返回ROLLBACK_MESSAGE,此时删除mq的此条消息
- 如果消息一定时间后没有被确认(COMMIT_MESSAGE)也没有被删除(ROLLBACK_MESSAGE),则mq回调一个方法,主动确认本地事务是否成功,主动要求确认消息状态。
1、配置文件
2、生产者
1、生产者配置
package com.gofun.customer.mqTrans; import com.gofun.customer.mq.RocketMqProducerProperties; import org.apache.rocketmq.client.exception.MQClientException; import org.apache.rocketmq.client.producer.TransactionMQProducer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.concurrent.*; @Configuration @EnableConfigurationProperties(RocketMqProducerProperties.class) @ConditionalOnProperty(prefix = "rocketmq.producer", name = "iseffect", havingValue = "true") public class RocketMqTransConfig { @Autowired private RocketMqProducerProperties rocketMqProperties; private final int corePoolSize = 2;//消费最小线程数 private final int maximumPoolSize = 5;//消费最大线程数 private final long keepAliveTime = 100;//线程活跃时间 private final TimeUnit timeUnit = TimeUnit.SECONDS;//keepAliveTime时间单位 private final int capacity = 2000;//保存任务的队列容量 @Bean @ConditionalOnProperty(prefix = "rocketmq.producer", name = "type", havingValue = "transaction") public TransactionMQProducer transactionMQProducer() throws MQClientException { TransactionMQProducer transactionMQProducer = new TransactionMQProducer(rocketMqProperties.getGroupName()); ExecutorService executorService = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, timeUnit, new ArrayBlockingQueue<Runnable>(capacity), new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r); thread.setName("client-transaction-msg-check-thread"); return thread; } }); transactionMQProducer.setNamesrvAddr(rocketMqProperties.getNamesrvAddr()); transactionMQProducer.setExecutorService(executorService); transactionMQProducer.start(); return transactionMQProducer; } }生产者发送工具类
View Codepackage com.gofun.customer.mqTrans; import com.alibaba.fastjson.JSON; import com.gofun.customer.mq.RocketMqProducerProperties; import org.apache.rocketmq.client.producer.DefaultMQProducer; import org.apache.rocketmq.client.producer.SendResult; import org.apache.rocketmq.client.producer.TransactionListener; import org.apache.rocketmq.client.producer.TransactionMQProducer; import org.apache.rocketmq.common.message.Message; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class RocketMqTransProducer { @Autowired private TransactionMQProducer transactionMQProducer; @Autowired private RocketMqProducerProperties rocketMqProperties; public void setListener(TransactionListener listener) { transactionMQProducer.setTransactionListener(listener); transactionMQProducer.setSendMsgTimeout(10000); } public SendResult send(String topic, String tag, Object obj) { Message msg = new Message(topic, tag, getBody(obj)); SendResult sendResult = null; try { sendResult = transactionMQProducer.sendMessageInTransaction(msg, null); } catch (Exception e) { } return sendResult; } public SendResult send(String tag, Object obj) { Message msg = new Message(rocketMqProperties.getTopicName(), tag, getBody(obj)); SendResult sendResult = null; try { sendResult = transactionMQProducer.sendMessageInTransaction(msg, null); } catch (Exception e) { } return sendResult; } private byte[] getBody(Object obj) { String body = null; if(obj == null){ return null; } if(obj instanceof String){ body = (String) obj; }else{ body = JSON.toJSONString(obj); } return body.getBytes(); } }2、本地事务和超时mq回调方法
package com.gofun.customer.controller.test; import org.apache.rocketmq.client.producer.LocalTransactionState; import org.apache.rocketmq.client.producer.TransactionListener; import org.apache.rocketmq.common.message.Message; import org.apache.rocketmq.common.message.MessageExt; import org.springframework.stereotype.Component; import java.util.Random; @Component public class TestTransactionListener implements TransactionListener { private static boolean transStatus = true; /** * 执行本地事务 * * @param message * @param o * @return */ @Override public LocalTransactionState executeLocalTransaction(Message message, Object o) { System.out.println("用一个随机数模拟本地任务执行成功或失败"); Random random = new Random(); transStatus = random.nextBoolean(); if (transStatus) { System.out.println("执行本地任务成功。。。。。。"); return LocalTransactionState.COMMIT_MESSAGE; } System.out.println("执行本地任务失败。。。。。"); return LocalTransactionState.ROLLBACK_MESSAGE; } /** * mq长时间收不到提交消息,会执行此方法,检查本地事务是否成功 * @param messageExt * @return */ @Override public LocalTransactionState checkLocalTransaction(MessageExt messageExt) { System.out.println("获取本地任务执行成功或失败"); if (transStatus) { System.out.println("判断本地任务成功。。。。"); return LocalTransactionState.COMMIT_MESSAGE; } System.out.println("判断本地任务失败。。。。"); return LocalTransactionState.ROLLBACK_MESSAGE; } }
- 实现接口TransactionListener
- executeLocalTransaction 方法内执行本地事务,并且判断事务是否成功,成功返回LocalTransactionState.COMMIT_MESSAGE,失败返回:LocalTransactionState.ROLLBACK_MESSAGE;
- LocalTransactionState.ROLLBACK_UNKNOW 是中间状态,该消息正在检查中,等待检查结果后执行上述两个状态
- checkLocalTransaction 方法是mq 长时间处于UNKNOW 状态时会调用此方法,主动请求确认消息状态。
3、发送事务消息
1、引入发送事务消息的工具类
@Autowired private RocketMqTransProducer rocketMqProducer; @Autowired private TestTransactionListener testTransactionListener;2、发送消息
rocketMqProducer.setListener(testTransactionListener); SendResult sendResult = rocketMqProducer.send("testTag", "测试mq发送消息。。。。"); if (sendResult != null) { SendStatus sendStatus = sendResult.getSendStatus(); System.out.println("发送消息返回:" + sendStatus.toString()); } else { System.out.println("发送消息失败"); }
3、消费者
消费者与非事务性消费者相同见:https://www.cnblogs.com/happydreamzjl/p/12022412.html
4、消费者消费情况
- 可看到本地任务成功时消费者消费了消息,本地任务失败时没有消费消息(消息没有发送成功)
转自:https://blog.csdn.net/Cy_LightBule/article/details/88891844