使用RocketMq事务消息解决分布式事务

事务消息

常见的事务处理方式有最终一致性,两阶段,XA,维护本地事务表等,而rocketmq消息中间件就是最终一致性的实现方式之一.

rocketmq是怎么实现的呢?
RocketMQ producer第一阶段发送Prepared消息时,会拿到消息的地址,第二阶段执行本地事物,第三阶段通过第一阶段拿到的地址去访问消息,并修改消息的状态,如果确认消息发送失败了,RocketMQ会定期扫描消息集群中的事物消息,如果发现了Prepared消息,它会向消息发送端(生产者)确认,A的钱到底是减了还是没减呢?如果减了是回滚还是继续发送确认消息呢?RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。这样就保证了消息发送与本地事务同时成功或同时失败。
==================================================================================

producer端:


 /**
     * 发送
     */
    @Test
    public void send1() {
        TransactionCheckListener transactionCheckListener = new TransactionCheckListenerImpl();
        TransactionMQProducer producer = new TransactionMQProducer("transaction_producer");
        producer.setNamesrvAddr("xxxx");
        producer.setTransactionCheckListener(transactionCheckListener);
        try {
            producer.start();
            String[] tags = new String[]{"TagA", "TagB"};
            TransactionExecuterImpl tranExecuter = new TransactionExecuterImpl();
            for (int i = 0; i < 3; i++) {
                Message msg = new Message("transactionTopic1", tags[i % 2], "uniqueId" + i,
                        ("Hello RocketMQ " + i).getBytes());
                SendResult sendResult = producer.sendMessageInTransaction(msg, tranExecuter, null);
                System.out.println(sendResult);
            }
            while (true) {
            }
        } catch (Exception e) {
            producer.shutdown();
            e.printStackTrace();
        }
    }

源码:


    /**
     * This method will be removed in the version 5.0.0, method <code>sendMessageInTransaction(Message,Object)</code>}
     * is recommended.
     */
    @Override
    @Deprecated
    public TransactionSendResult sendMessageInTransaction(final Message msg,
        final LocalTransactionExecuter tranExecuter, final Object arg) throws MQClientException {
        if (null == this.transactionCheckListener) {
            throw new MQClientException("localTransactionBranchCheckListener is null", null);
        }

        msg.setTopic(NamespaceUtil.wrapNamespace(this.getNamespace(), msg.getTopic()));
        return this.defaultMQProducerImpl.sendMessageInTransaction(msg, tranExecuter, arg);
    }

    @Override
    public TransactionSendResult sendMessageInTransaction(final Message msg,
        final Object arg) throws MQClientException {
        if (null == this.transactionListener) {
            throw new MQClientException("TransactionListener is null", null);
        }

        msg.setTopic(NamespaceUtil.wrapNamespace(this.getNamespace(), msg.getTopic()));
        return this.defaultMQProducerImpl.sendMessageInTransaction(msg, null, arg);
    }

consumer端:

/**
     * 消费
     */
    @Test
    public void consumer1() {
        DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("transaction_consumer");
        consumer.setNamesrvAddr("xxxx");
        consumer.setConsumeFromWhere(ConsumeFromWhere.CONSUME_FROM_FIRST_OFFSET);
        try {
            consumer.subscribe("transactionTopic1", "*");
            consumer.registerMessageListener(new MessageListenerConcurrently() {
                public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                    try {
                        System.out.println("transactionInfo:" + new String(msgs.get(0).getBody()));
                    } catch (Exception e) {
                        e.printStackTrace();
                        return ConsumeConcurrentlyStatus.RECONSUME_LATER;// 重试
                    }
                    return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;// 成功
                }
            });

            consumer.start();
            System.out.println("transaction_consumer started.");
            while (true) {
            }
        } catch (MQClientException e) {
            e.printStackTrace();
        }
    }

本地事务:

public class TransactionExecuterImpl implements LocalTransactionExecuter {
    @Override
    public LocalTransactionState executeLocalTransactionBranch(final Message msg, final Object arg) {
        System.out.println("执行本地事务msg = " + new String(msg.getBody()) + "   ;arg = " + arg);
        String tags = msg.getTags();
        if (tags.equals("TagB")) {
            System.out.println("进行-------ROLLBACK");
            return LocalTransactionState.ROLLBACK_MESSAGE;//回滚
//            return LocalTransactionState.UNKNOW;//未决事务
        }
        return LocalTransactionState.COMMIT_MESSAGE;//提交
    }


}

未决事务:

public class TransactionCheckListenerImpl implements TransactionCheckListener {
    public LocalTransactionState checkLocalTransactionState(MessageExt msg) {
        try {
            //TODO 根据业务处理
            System.out.println("未决事务,服务器回查客户端msg =" + new String(msg.getBody(),"utf-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
         return LocalTransactionState.ROLLBACK_MESSAGE;
//        return LocalTransactionState.COMMIT_MESSAGE;
//         return LocalTransactionState.UNKNOW;
    }
}

涉及的源码附上:

public interface TransactionListener {
    /**
     * When send transactional prepare(half) message succeed, this method will be invoked to execute local transaction.
     *
     * @param msg Half(prepare) message
     * @param arg Custom business parameter
     * @return Transaction state
     *这个事务处理方法
     */
    LocalTransactionState executeLocalTransaction(final Message msg, final Object arg);

    /**
     * When no response to prepare(half) message. broker will send check message to check the transaction status, and this
     * method will be invoked to get local transaction status.
     *rocket默认固定间隔60s调用这个方法来校验本地事务状态
     * @param msg Check message
     * @return Transaction state
     */
    LocalTransactionState checkLocalTransaction(final MessageExt msg);
}

猜你喜欢

转载自blog.csdn.net/qq_41988225/article/details/103859268