RocketMQ事务性消息

mq事务介绍

mq事务消息流程

  1. 生产者发送消息到mq,消息状态为:SEND_OK。此消息是消费者不可见(消费者无法消费此条消息)
  2. 执行本地任务:成功则返回COMMIT_MESSAGE,此时消费者可消费此条消息。失败则返回ROLLBACK_MESSAGE,此时删除mq的此条消息
  3. 如果消息一定时间后没有被确认(COMMIT_MESSAGE)也没有被删除(ROLLBACK_MESSAGE),则mq回调一个方法,主动确认本地事务是否成功,主动要求确认消息状态。

1、配置文件

详情见:https://www.cnblogs.com/happydreamzjl/p/12022412.html

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;
    }

}

生产者发送工具类

package 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();
    }

}
View Code

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;
    }
}
  1. 实现接口TransactionListener
  2. executeLocalTransaction 方法内执行本地事务,并且判断事务是否成功,成功返回LocalTransactionState.COMMIT_MESSAGE,失败返回:LocalTransactionState.ROLLBACK_MESSAGE;
  3. LocalTransactionState.ROLLBACK_UNKNOW 是中间状态,该消息正在检查中,等待检查结果后执行上述两个状态
  4. 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、消费者消费情况

image

  1. 可看到本地任务成功时消费者消费了消息,本地任务失败时没有消费消息(消息没有发送成功)

转自:https://blog.csdn.net/Cy_LightBule/article/details/88891844

        https://www.jianshu.com/p/5260a2739d80

猜你喜欢

转载自www.cnblogs.com/happydreamzjl/p/12023625.html