消息队列-RockMQ-事务消息收发实践

事务消息收发实战

场景介绍

事务是一组操作组成,一个原子操作要么都成功,要么都失败。

如果任务是在本地,比如操作数据库,可以把一组操作放到事务里面。但是发送消息是网络远程操作,怎么保证事务呢?

正常的思路可能是先要用一张表来记录每一步骤的状态,然后监听这个状态来处理。举个常见的例子比如转账业务,我们发送一条消息到MQ,这条消息是远程累加B的余额,在累加B的余额之前我们要扣除A的余额扣除成功后这条消息才能到消费方执行真正的逻辑增加B的余额,这两个操作是一个事务操作。那么RocketMq提供了怎样的能力实现这个场景呢,看下面这个图:

在这里插入图片描述

代码实操

生产者

public class Producer {
    public static void main(String[] args) throws Exception {
        TransactionMQProducer producer = new TransactionMQProducer("producer_group_trans");
        producer.setNamesrvAddr("ip:9876");
        producer.setTransactionListener(new TransactionListener() {
            @Override
            public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
                // 这里需要写真正的业务逻辑
                // 如果业务执行成功就返回成功,提交事务
                // 如果业务执行失败就返回失败,回滚事务
                System.out.println("============================ 检查事务状态 ===========================");
                try {
                    boolean state = getState();
                    if (state) {
                        System.out.println("================= 成功提交了消息:" + new String(msg.getBody()));
                        return LocalTransactionState.COMMIT_MESSAGE;
                    }
                    System.out.println("================= 回滚消息:" + new String(msg.getBody()));
                    return LocalTransactionState.ROLLBACK_MESSAGE;
                } catch (Exception e) {
                    e.printStackTrace();
                    System.out.println("================= 发生了异常,要进行异常检查 " + new String(msg.getBody()));
                    // 发生异常这里状态这是为UNKNOW 过一段时间就会触发checkLocalTransaction去尽显再次确认
                    return LocalTransactionState.UNKNOW;
                }
            }
            @Override
            public LocalTransactionState checkLocalTransaction(MessageExt msg) {
                System.out.println("============================ 异常检查机制 ===========================");
                // 当发生异常了这里来判断到底业务到底是否执行成功
                String property = msg.getProperty("state");
                if ("true".equals(property)) {
                    System.out.println("================= 异常检查通过,成功发送消息:" + new String(msg.getBody()));
                    return LocalTransactionState.COMMIT_MESSAGE;
                }
                return LocalTransactionState.ROLLBACK_MESSAGE;
            }
        });
        producer.start();
        // 生成3大类消息
        List<Order> F = OrderBuilder.build(1, 4, "TAG1", "A");
        List<Order> S = OrderBuilder.build(5, 4, "TAG2", "B");
        List<Order> T = OrderBuilder.build(9, 4, "TAG3", "C");
        ArrayList<Order> orders = new ArrayList<Order>() {{
            addAll(F);
            addAll(S);
            addAll(T);
        }};
        for (Order order : orders) {
            Message msg = new Message("transaction-topic", order.getTag(), order.toString().getBytes());
            msg.setKeys("transaction-topic-trace");
            // 当发生异常的时候我们模拟 id % 5的消息就算异常了事务也执行成功
            if (order.getOrderID() % 5 == 0) {
                msg.putUserProperty("state", String.valueOf(true));
            }
            producer.sendMessageInTransaction(msg, null);
        }
    }

    /**
     * 模拟操作DB 三种状态 成功 失败 发生异常
     * @return
     * @throws IllegalArgumentException
     */
    public static boolean getState() throws IllegalArgumentException {
        Random random = new Random();
        int randomNumber = random.nextInt(3);
        if (randomNumber == 0) {
            return true;
        } else if (randomNumber == 1) {
            return  false;
        }
        throw new IllegalArgumentException();
    }
}

我们可以得到下面的日志:

============================ 检查事务状态 ===========================
================= 成功提交了消息:orderID 1 desc A tag TAG1 state 2024-01-09 10:59:35 sendTime
============================ 检查事务状态 ===========================
================= 回滚消息:orderID 2 desc A tag TAG1 state 2024-01-09 10:59:36 sendTime
============================ 检查事务状态 ===========================
================= 发生了异常,要进行异常检查 orderID 3 desc A tag TAG1 state 2024-01-09 10:59:36 sendTime
============================ 检查事务状态 ===========================
================= 回滚消息:orderID 4 desc A tag TAG1 state 2024-01-09 10:59:36 sendTime
java.lang.IllegalArgumentException
	at com.liyong.learn.Producer.getState(Producer.java:99)
	at com.liyong.learn.Producer$1.executeLocalTransaction(Producer.java:36)
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendMessageInTransaction(DefaultMQProducerImpl.java:1264)
	at org.apache.rocketmq.client.producer.TransactionMQProducer.sendMessageInTransaction(TransactionMQProducer.java:94)
	at com.liyong.learn.Producer.main(Producer.java:79)
============================ 检查事务状态 ===========================
================= 回滚消息:orderID 5 desc B tag TAG2 state 2024-01-09 10:59:36 sendTime
============================ 检查事务状态 ===========================
================= 成功提交了消息:orderID 6 desc B tag TAG2 state 2024-01-09 10:59:36 sendTime
============================ 检查事务状态 ===========================
================= 成功提交了消息:orderID 7 desc B tag TAG2 state 2024-01-09 10:59:36 sendTime
============================ 检查事务状态 ===========================
================= 回滚消息:orderID 8 desc B tag TAG2 state 2024-01-09 10:59:36 sendTime
============================ 检查事务状态 ===========================
================= 成功提交了消息:orderID 9 desc C tag TAG3 state 2024-01-09 10:59:36 sendTime
============================ 检查事务状态 ===========================
================= 发生了异常,要进行异常检查 orderID 10 desc C tag TAG3 state 2024-01-09 10:59:36 sendTime
============================ 检查事务状态 ===========================
================= 发生了异常,要进行异常检查 orderID 11 desc C tag TAG3 state 2024-01-09 10:59:36 sendTime
============================ 检查事务状态 ===========================
================= 回滚消息:orderID 12 desc C tag TAG3 state 2024-01-09 10:59:36 sendTime
java.lang.IllegalArgumentException
	at com.liyong.learn.Producer.getState(Producer.java:99)
	at com.liyong.learn.Producer$1.executeLocalTransaction(Producer.java:36)
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendMessageInTransaction(DefaultMQProducerImpl.java:1264)
	at org.apache.rocketmq.client.producer.TransactionMQProducer.sendMessageInTransaction(TransactionMQProducer.java:94)
	at com.liyong.learn.Producer.main(Producer.java:79)
java.lang.IllegalArgumentException
	at com.liyong.learn.Producer.getState(Producer.java:99)
	at com.liyong.learn.Producer$1.executeLocalTransaction(Producer.java:36)
	at org.apache.rocketmq.client.impl.producer.DefaultMQProducerImpl.sendMessageInTransaction(DefaultMQProducerImpl.java:1264)
	at org.apache.rocketmq.client.producer.TransactionMQProducer.sendMessageInTransaction(TransactionMQProducer.java:94)
	at com.liyong.learn.Producer.main(Producer.java:79)
============================ 异常检查机制 ===========================
================= 异常检查通过,成功发送消息:orderID 10 desc C tag TAG3 state 2024-01-09 10:59:36 sendTime
============================ 异常检查机制 ===========================
============================ 异常检查机制 ===========================

这个日志我们分析知道模拟的事务操作成功执行了4次,分别为1,6,7,9 然后有3条发生了异常(不确定事务有没有执行成功)为3,10,11这个时候过一段时间就会进入checkLocalTransaction这个方法,我们的模拟逻辑是如果模于5为0则也提交消息。其它的消息事务都执行失败,所以消费者最终得到了1,6,7,9,10这几条消息进行后面的逻辑处理,这样可以看到保证了A这边的事务执行成功了,再到消费者去执行B后面的逻辑也要保证B成功否则还是需要回滚。RocketMQ保证了消费者不会受到A这边事务已经执行失败的消息。
消费者

public class Consumer {
    public static void main(String[] args) throws Exception{
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("transaction-topic-group");
        consumer.setNamesrvAddr("ip:9876");
        consumer.subscribe("transaction-topic", "*");
        consumer.registerMessageListener(new MessageListenerOrderly() {
            @Override
            public ConsumeOrderlyStatus consumeMessage(List<MessageExt> msgs, ConsumeOrderlyContext context) {
                for (MessageExt msg : msgs) {
                    System.out.println(String.format("msg {%s} recvTime %s %s", new String(msg.getBody()), new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()), msg.getTags()));
                }
                return ConsumeOrderlyStatus.SUCCESS;
            }
        });
        consumer.start();
    }
}

在这里插入图片描述

  • 24
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值