携手创作,共同成长!这是我参与「掘金日新计划 · 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具体实现如下:
RocketMQTemplate的sendMessageInTransaction调用的接口是TransactionMQProducer的sendMessageInTransaction其具体实现如下:
调用了DefaultMQProducerImpl中的sendMessageInTransaction具体实现如下:
通过getCheckListener获取事务的监听器具体实现如下:
从源码中我们可以分析得知,监听器从生产者中进行获取的,那么生产者中的监听器是如何注入的呢?
通过相关源码分析,我们得知生者者注册监听器在RocketMQTransactionConfiguration中的registerTransactionListener中实现的,其具体的源码如下:
说明:通过循环注册监听器,如果配置多个则会报如下错误,这个特性是Spring Boot2.0的版本之后针对@RocketMQTransactionListener移除txProducerGroup属性和对sendMessageInTransaction方法,所以我们可以得知,一个Producer只能对应一个监听器,而Producer在RocketMQTemplate中进行申明的,所以我们发送多个事务消息,需要定义多个RocketMQTemplate实例,那么我们如何实现呢?
我们查看下RocketMQTransactionListener注解的定义如下:
通过注解的说明,可以得知如果实现多个监听,需要设置不同的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发送多个事务消息进行了消息讲解,如有疑问请随时反馈。