6. rocketmq事务消息

什么是事务消息

事务消息用于解决分布式系统中的事务问题,不了解分布式事务的请自行Google。

通常分布式事务可以使用两阶段,三阶段,TCC,XA,本地事务表等方式来实现强一致性或者最终一致性事务。

这里rocketmq的事务消息就是采用的最终一致性解决的分布式事务。
分布式事务的两个参与者,一方参与者通过事务消息保证本地事务执行结果与MQ中的消息一致,要么都成功,要么都失败回滚。
另一个参与者则消费MQ中的消息,注意offset的提交,需要保证消费不丢失以及支持幂等。

rocketmq的执行流程如下:
在这里插入图片描述

接下来看一下使用事务消息。

生产者

与之前的生产者不同,这里使用的是支持事务的生产者TransactionMQProducer,并且需要编写一个监听器transactionListener,用于执行本地事务的提交或MQ的broker在超时获取不到提交或回滚指令后检查消息状态。

public static void main(String[] args) throws MQClientException, InterruptedException {
        TransactionListener transactionListener = new TransactionListenerImpl();
        TransactionMQProducer producer = new TransactionMQProducer("please_rename_unique_group_name");//这里使用的是支持事务的TransactionMQProducer 
        producer.setNamesrvAddr("node1:9876");
        ExecutorService executorService = new ThreadPoolExecutor(2, 5, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<>(2000), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
                thread.setName("client-transaction-msg-check-thread");
                return thread;
            }
        });

        producer.setExecutorService(executorService);
        producer.setTransactionListener(transactionListener);//配置监听器
        producer.start();

        String[] tags = new String[]{"TagA", "TagB", "TagC", "TagD", "TagE"};
        for (int i = 0; i < 10; i++) {
            try {
                Message msg =
                        new Message("TopicTest", tags[i % tags.length], "KEY" + i,
                                ("Hello RocketMQ " + i).getBytes(RemotingHelper.DEFAULT_CHARSET));
                SendResult sendResult = producer.sendMessageInTransaction(msg, null);
                System.out.printf("%s%n", sendResult);

                Thread.sleep(10);
            } catch (MQClientException | UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }

        TimeUnit.SECONDS.sleep(60);
        producer.shutdown();
    }


public class TransactionListenerImpl implements TransactionListener {
       private AtomicInteger transactionIndex = new AtomicInteger(0);
   
       private ConcurrentHashMap<String, Integer> localTrans = new ConcurrentHashMap<>();
   
       //执行本地事务
       @Override
       public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
           System.out.println("executeLocalTransaction");
           int value = transactionIndex.getAndIncrement();
           int status = value % 3;//这里模拟失败超时等场景
           localTrans.put(msg.getTransactionId(), status);
           return LocalTransactionState.UNKNOW;
       }
   
   //检查本地事务执行结果
       @Override
       public LocalTransactionState checkLocalTransaction(MessageExt msg) {
           Integer status = localTrans.get(msg.getTransactionId());
           System.out.println("checkLocalTransaction" + msg + "   "+status);
           if (null != status) {
               switch (status) {
                   case 0:
                       return LocalTransactionState.UNKNOW;
                   case 1:
                       return LocalTransactionState.COMMIT_MESSAGE;
                   case 2:
                       return LocalTransactionState.ROLLBACK_MESSAGE;
               }
           }
           return LocalTransactionState.COMMIT_MESSAGE;
       }
   }

消费者

这里还是采用之前的普通消费者

public static void main(String[] args) throws Exception {
        normal();//普通消费

//        order();//顺序消费
    }

    private static void normal() throws MQClientException {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("example_group_name");
        consumer.setNamesrvAddr("node1:9876");

        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);

        consumer.subscribe("TopicTest", "*");
        consumer.setConsumeThreadMin(3);
        consumer.setConsumeThreadMax(6);

        consumer.registerMessageListener((MessageListenerConcurrently) (msgs, context) -> {
            for (MessageExt msg : msgs) {
//                    System.out.println("收到消息," + new String(msg.getBody()));
                System.out.println("queueId:"+msg.getQueueId()+",orderId:"+new String(msg.getBody())+",i:"+msg.getKeys());
            }
            return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
        });

        consumer.start();

        System.out.printf("Consumer Started.%n");
    }

先启动消费者等待消费数据。

然后启动生产者,生产消息。注意这里在执行本地事务时模拟了提交回滚未知等情况,所以实际成功的消息有7条,大家可以根据msgId来查看生产者成功的数据是否与消费者收到的数据一致。

发布了233 篇原创文章 · 获赞 211 · 访问量 90万+

猜你喜欢

转载自blog.csdn.net/fgyibupi/article/details/104063871