Spring Boot封装RocketMQ事务消息发送

下面我将把RocketMQ事务消息发送功能封装为Spring Boot组件,方便在项目中调用。

1. 添加依赖

首先确保pom.xml中有RocketMQ客户端依赖:

<dependency>
    <groupId>org.apache.rocketmq</groupId>
    <artifactId>rocketmq-client-java</artifactId>
    <version>5.0.8</version>
</dependency>

2. 配置类

import org.apache.rocketmq.client.apis.ClientServiceProvider;
import org.apache.rocketmq.client.apis.producer.Producer;
import org.apache.rocketmq.client.apis.producer.TransactionChecker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RocketMQConfig {
    private static final Logger log = LoggerFactory.getLogger(RocketMQConfig.class);

    @Value("${rocketmq.transaction.topic:yourTransactionTopic}")
    private String transactionTopic;

	# 或者使用自定义的 9878 端口
    @Value("${rocketmq.endpoints:localhost:8081}")
    private String endpoints;

    @Bean
    public ClientServiceProvider clientServiceProvider() {
        return ClientServiceProvider.loadService();
    }

    @Bean
    public TransactionChecker transactionChecker() {
        return messageView -> {
            log.info("Receive transactional message check, message={}", messageView);
            // 这里可以根据业务逻辑决定是提交还是回滚
            // 可以从messageView中获取业务数据进行检查
            return TransactionResolution.COMMIT;
        };
    }

    @Bean(destroyMethod = "close")
    public Producer transactionalProducer(ClientServiceProvider provider, TransactionChecker checker) {
        try {
            return provider.newProducerBuilder()
                    .setTopics(transactionTopic)
                    .setTransactionChecker(checker)
                    .build();
        } catch (Exception e) {
            log.error("Failed to create transactional producer", e);
            throw new RuntimeException("Failed to create transactional producer", e);
        }
    }
}

3. 服务类封装

import org.apache.rocketmq.client.apis.ClientServiceProvider;
import org.apache.rocketmq.client.apis.message.Message;
import org.apache.rocketmq.client.apis.producer.Producer;
import org.apache.rocketmq.client.apis.producer.Transaction;
import org.apache.rocketmq.client.apis.producer.TransactionResolution;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.nio.charset.StandardCharsets;
import java.util.Map;

@Service
public class RocketMQTransactionalService {
    private static final Logger log = LoggerFactory.getLogger(RocketMQTransactionalService.class);

    private final ClientServiceProvider provider;
    private final Producer producer;
    private final String transactionTopic;

    @Autowired
    public RocketMQTransactionalService(ClientServiceProvider provider, 
                                      Producer producer,
                                      @Value("${rocketmq.transaction.topic:yourTransactionTopic}") String transactionTopic) {
        this.provider = provider;
        this.producer = producer;
        this.transactionTopic = transactionTopic;
    }

    /**
     * 发送事务消息
     * @param body 消息内容
     * @param tag 消息标签
     * @param keys 消息keys
     * @param properties 消息属性
     * @return 事务对象,用于后续提交或回滚
     */
    public Transaction sendTransactionalMessage(String body, String tag, String keys, Map<String, String> properties) {
        try {
            Message.Builder builder = provider.newMessageBuilder()
                    .setTopic(transactionTopic)
                    .setTag(tag)
                    .setKeys(keys)
                    .setBody(body.getBytes(StandardCharsets.UTF_8));

            if (properties != null) {
                properties.forEach(builder::addProperty);
            }

            Message message = builder.build();
            Transaction transaction = producer.beginTransaction();
            producer.send(message, transaction);
            log.info("Send transaction message successfully, keys={}", keys);
            return transaction;
        } catch (Exception e) {
            log.error("Failed to send transactional message", e);
            throw new RuntimeException("Failed to send transactional message", e);
        }
    }

    /**
     * 提交事务
     * @param transaction 事务对象
     */
    public void commitTransaction(Transaction transaction) {
        try {
            transaction.commit();
            log.info("Transaction committed successfully");
        } catch (Exception e) {
            log.error("Failed to commit transaction", e);
            throw new RuntimeException("Failed to commit transaction", e);
        }
    }

    /**
     * 回滚事务
     * @param transaction 事务对象
     */
    public void rollbackTransaction(Transaction transaction) {
        try {
            transaction.rollback();
            log.info("Transaction rolled back successfully");
        } catch (Exception e) {
            log.error("Failed to rollback transaction", e);
            throw new RuntimeException("Failed to rollback transaction", e);
        }
    }
}

4. 使用示例

import org.apache.rocketmq.client.apis.producer.Transaction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
public class OrderService {
    @Autowired
    private RocketMQTransactionalService rocketMQTransactionalService;

    public void createOrder(Order order) {
        // 1. 准备消息内容
        String messageBody = "Order created: " + order.getId();
        String tag = "ORDER_CREATED";
        String keys = "order-" + order.getId();
        
        // 可以添加自定义属性
        Map<String, String> properties = new HashMap<>();
        properties.put("orderId", order.getId());
        properties.put("userId", order.getUserId());

        // 2. 发送事务消息
        Transaction transaction = null;
        try {
            transaction = rocketMQTransactionalService.sendTransactionalMessage(
                messageBody, tag, keys, properties);
            
            // 3. 执行本地事务
            boolean localTransactionSuccess = processLocalTransaction(order);
            
            // 4. 根据本地事务结果提交或回滚
            if (localTransactionSuccess) {
                rocketMQTransactionalService.commitTransaction(transaction);
            } else {
                rocketMQTransactionalService.rollbackTransaction(transaction);
            }
        } catch (Exception e) {
            if (transaction != null) {
                rocketMQTransactionalService.rollbackTransaction(transaction);
            }
            throw e;
        }
    }

    private boolean processLocalTransaction(Order order) {
        // 这里执行本地事务逻辑,如数据库操作等
        // 返回true表示成功,false表示失败
        return true;
    }
}

5. 事务检查器实现

如果需要更复杂的事务检查逻辑,可以自定义实现:

import org.apache.rocketmq.client.apis.message.MessageView;
import org.apache.rocketmq.client.apis.producer.TransactionChecker;
import org.apache.rocketmq.client.apis.producer.TransactionResolution;
import org.springframework.stereotype.Component;

@Component
public class CustomTransactionChecker implements TransactionChecker {
    @Override
    public TransactionResolution check(MessageView messageView) {
        // 从消息中获取业务数据
        String orderId = messageView.getProperties().get("orderId");
        
        // 检查本地事务状态
        boolean isSuccess = checkLocalTransactionStatus(orderId);
        
        return isSuccess ? TransactionResolution.COMMIT : TransactionResolution.ROLLBACK;
    }

    private boolean checkLocalTransactionStatus(String orderId) {
        // 实现检查本地事务状态的逻辑
        // 例如查询数据库确认订单状态
        return true;
    }
}

然后在配置类中修改:

@Bean
public TransactionChecker transactionChecker(CustomTransactionChecker customChecker) {
    return customChecker;
}

6. 配置参数

application.yml中添加配置:

rocketmq:
  endpoints: localhost:8081
  transaction:
    topic: yourTransactionTopic

总结

这样封装后,在Spring Boot项目中就可以方便地使用RocketMQ事务消息了:

  1. 注入RocketMQTransactionalService
  2. 调用sendTransactionalMessage发送消息并获取事务对象
  3. 执行本地事务
  4. 根据本地事务结果调用commitTransactionrollbackTransaction