Spring Boot集成RocketMq如何发送多个事务消息?

1,686 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第22天,点击查看活动详情

背景

前不久写了一篇(juejin.cn/post/710498…) Spring Boot集成RocketMq发送事务消息的文章,得到好友的支持和点赞,同时好友给我留言,如何在一个项目中发送多个事务消息?针对这个问题,我通过查阅相关资料和源码,找到了合适的解决方案,本文将详细的进行分析和解答。

问题原因

发送事务消息的代码

@Component
public class TransactionProduce
{
    private Logger logger = LoggerFactory.getLogger(getClass());
    
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    
    public void sendTransactionMessage(String msg)
    {
        logger.info("product start sendTransMessage msg:{}",msg);
       
         Message message =new Message();
         message.setTopic("test-tx-rocketmq");
         message.setBody("this is tx message".getBytes());
         message.setTags("TAGA");
         TransactionSendResult result=rocketMQTemplate.sendMessageInTransaction("test-tx-rocketmq", 
                 MessageBuilder.withPayload(message).build(), msg);
         
         //发送状态
         String sendStatus = result.getSendStatus().name();
         // 本地事务执行状态
         String localTxState = result.getLocalTransactionState().name();
         logger.info("send tx message sendStatus:{},localTXState:{}",sendStatus,localTxState);
    } 
}

说明:通过调用rocketMQTemplate的sendMessageInTransaction可以发送事务消息,而sendMessageInTransaction具体实现如下:

图片.png

RocketMQTemplate的sendMessageInTransaction调用的接口是TransactionMQProducer的sendMessageInTransaction其具体实现如下:

图片.png

调用了DefaultMQProducerImpl中的sendMessageInTransaction具体实现如下:

图片.png

通过getCheckListener获取事务的监听器具体实现如下:

图片.png

从源码中我们可以分析得知,监听器从生产者中进行获取的,那么生产者中的监听器是如何注入的呢?

通过相关源码分析,我们得知生者者注册监听器在RocketMQTransactionConfiguration中的registerTransactionListener中实现的,其具体的源码如下:

图片.png

说明:通过循环注册监听器,如果配置多个则会报如下错误,这个特性是Spring Boot2.0的版本之后针对@RocketMQTransactionListener移除txProducerGroup属性和对sendMessageInTransaction方法,所以我们可以得知,一个Producer只能对应一个监听器,而Producer在RocketMQTemplate中进行申明的,所以我们发送多个事务消息,需要定义多个RocketMQTemplate实例,那么我们如何实现呢?

图片.png

我们查看下RocketMQTransactionListener注解的定义如下:

图片.png

通过注解的说明,可以得知如果实现多个监听,需要设置不同的rocketMQTemplate即可

具体实现方案

定义扩展的rocketmqTemalte

@ExtRocketMQTemplateConfiguration
public class ExtRocketMQTemplate extends RocketMQTemplate
{

}

注意:一定要使用@ExtRocketMQTemplateConfiguration扩展配置注解。

定义多个监听器

//默认监听器
@RocketMQTransactionListener
public class TransactionMsgListener implements RocketMQLocalTransactionListener
{
    private Logger logger = LoggerFactory.getLogger(getClass());
    
    /**
     * 执行本地事务
     */
    @Override
    public RocketMQLocalTransactionState executeLocalTransaction(Message msg,
            Object obj)
    {
        logger.info("start invoke local rocketMQ transaction");
        RocketMQLocalTransactionState resultState = RocketMQLocalTransactionState.UNKNOWN;
        
        try
        {
            //处理业务
            String jsonStr = new String((byte[]) msg.getPayload(), StandardCharsets.UTF_8);
            logger.info("invoke local transaction msg content:{}",jsonStr);
        }
        catch (Exception e)
        {
            logger.error("invoke local mq trans error",e);
            resultState = RocketMQLocalTransactionState.UNKNOWN;
        }
        
        return resultState;
    }

    /**
     * 检查本地事务的状态
     */
    @Override
    public RocketMQLocalTransactionState checkLocalTransaction(Message msg)
    {
        logger.info("start check Local rocketMQ transaction");
        
        RocketMQLocalTransactionState resultState = RocketMQLocalTransactionState.UNKNOWN;
        
        try
        {
            String jsonStr = new String((byte[]) msg.getPayload(), StandardCharsets.UTF_8);
            logger.info("check trans msg content:{}",jsonStr);
        }
        catch (Exception e)
        {
            resultState  = RocketMQLocalTransactionState.ROLLBACK;
        }
        return resultState;
    }
}

//扩展监听器
@RocketMQTransactionListener(rocketMQTemplateBeanName="extRocketMQTemplate")
public class TransactionMsgListener1 implements RocketMQLocalTransactionListener
{
    private Logger logger = LoggerFactory.getLogger(getClass());
    
    /**
     * 执行本地事务
     */
    @Override
    public RocketMQLocalTransactionState executeLocalTransaction(Message msg,
            Object obj)
    {
        logger.info("start invoke local rocketMQ transaction");
        RocketMQLocalTransactionState resultState = RocketMQLocalTransactionState.COMMIT;
        
        try
        {
            //处理业务
            String jsonStr = new String((byte[]) msg.getPayload(), StandardCharsets.UTF_8);
            logger.info("invoke local transaction msg content:{}",jsonStr);
        }
        catch (Exception e)
        {
            logger.error("invoke local mq trans error",e);
            resultState = RocketMQLocalTransactionState.UNKNOWN;
        }
        
        return resultState;
    }

    /**
     * 检查本地事务的状态
     */
    @Override
    public RocketMQLocalTransactionState checkLocalTransaction(Message msg)
    {
        logger.info("start check Local rocketMQ transaction");
        
        RocketMQLocalTransactionState resultState = RocketMQLocalTransactionState.COMMIT;
        
        try
        {
            String jsonStr = new String((byte[]) msg.getPayload(), StandardCharsets.UTF_8);
            logger.info("check trans msg content:{}",jsonStr);
        }
        catch (Exception e)
        {
            resultState  = RocketMQLocalTransactionState.ROLLBACK;
        }
        return resultState;
    }
}

注意:定义监听器时需要指定rocketMQTemplateBeanName的名称。否则启动时会报错。

消息发送者

//发送默认的事务消息

@Component
public class TransactionProduce
{
    private Logger logger = LoggerFactory.getLogger(getClass());
    
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    
    public void sendTransactionMessage(String msg)
    {
        logger.info("product start sendTransMessage msg:{}",msg);
       
         Message message =new Message();
         message.setTopic("test-tx-rocketmq");
         message.setBody("this is tx message".getBytes());
         message.setTags("TAGA");
         TransactionSendResult result=rocketMQTemplate.sendMessageInTransaction("test-tx-rocketmq", 
                 MessageBuilder.withPayload(message).build(), msg);
         
         //发送状态
         String sendStatus = result.getSendStatus().name();
         // 本地事务执行状态
         String localTxState = result.getLocalTransactionState().name();
         logger.info("send tx message sendStatus:{},localTXState:{}",sendStatus,localTxState);
    } 
}

//发送扩展事务消息
@Component
public class TransactionProduce1
{
    private Logger logger = LoggerFactory.getLogger(getClass());
    
    @Autowired
    private ExtRocketMQTemplate extRocketMQTemplate;
    
    public void sendTransactionMessage(String msg)
    {
        logger.info("product start sendTransMessage msg:{}",msg);
       
         Message message =new Message();
         message.setTopic("test-tx-rocketmq1");
         message.setBody("this is tx message".getBytes());
         message.setTags("TAGA");
         TransactionSendResult result=extRocketMQTemplate.sendMessageInTransaction("test-tx-rocketmq1", 
                 MessageBuilder.withPayload(message).build(), msg);
         
         //发送状态
         String sendStatus = result.getSendStatus().name();
         // 本地事务执行状态
         String localTxState = result.getLocalTransactionState().name();
         logger.info("send tx message sendStatus:{},localTXState:{}",sendStatus,localTxState);
    } 
}

说明:发送消息的内容基本一致,唯一的区别在于采用的是不同的模板。

消息消费者

//消费者1

@Component
@RocketMQMessageListener(consumerGroup="test-txRocketmq-group",topic="test-tx-rocketmq", messageModel = MessageModel.CLUSTERING)
public class TransactionConsumer implements RocketMQListener<String>
{
    private Logger logger =LoggerFactory.getLogger(getClass());
    @Override
    public void onMessage(String message)
    {
        logger.info("send transaction mssage parma is:{}", message);
    }
}

//消费者2

@Component
@RocketMQMessageListener(consumerGroup="test-txRocketmq-group1",topic="test-tx-rocketmq1", messageModel = MessageModel.CLUSTERING)
public class TransactionConsumer1 implements RocketMQListener<String>
{
    private Logger logger =LoggerFactory.getLogger(getClass());
    @Override
    public void onMessage(String message)
    {
        logger.info("send two transaction mssage parma is:{}", message);
    }
}

说明:消费者的实现无差别,只需要定义接收消息的topic不同而已即可。

总结

本文针对Spring Boot集成RocketMq发送多个事务消息进行了消息讲解,如有疑问请随时反馈。