事务消息
RocketMQ在4.3版本后已经支持了分布式事务消息了
RocketMQ采用了两阶段提交(2PC)的思想来实现了事务消息,同时增加一个补偿逻辑来处理二阶段超时或者失败的消息
事务消息的流程
1、生产者发送一条事务消息,这条消息在发送后不会立即送到用户指定的topic中,而是将其发送到主题为RMQ_SYS_TRANS_HALF_TOPIC
的主题中,这样消费者就不会收到这条消息去处理了
2、接着就看用户的本地事务执行情况
成功
发送commit请求,rocketmq就会把这条放在RMQ_SYS_TRANS_HALF_TOPIC
主题中的消息放到用户指定的topic中
消费者就可以收到这条消息并处理了
正常失败
发送rollback请求,rocketmq就会把这条消息丢弃
超时失败
rocketmq会主动询问生产者,主动进行事务状态回查,默认回查15次,如果都失败,则直接回滚这条消息
只有在确保该消息事务被生产者提交成功后才会向broker发送消息,从而被消费者获取并进行消费
事务状态
LocalTransactionState.COMMIT_MESSAGE
:事务已提交,消费者可消费LocalTransactionState.ROLLBACK_MESSAGE
:回滚事务,这条消息会被删除,不允许被消费者消费LocalTransactionState.UNKNOW
:中间状态,它代表需要检查消息队列来确定状态
流程图
案例说明
上个案例中普通的DefaultMQProducer是不支持发送事务消息的
所以我们把MQProducer的实现换一下
@Bean
public TransactionMQProducer transactionMQProducer() {
TransactionMQProducer transactionMQProducer = new TransactionMQProducer(rocketMqProducerProperties.getGroupName());
transactionMQProducer.setNamesrvAddr(rocketMqProducerProperties.getNamesrvAddr());
transactionMQProducer.setMaxMessageSize(rocketMqProducerProperties.getMaxMessageSize());
transactionMQProducer.setSendMsgTimeout(rocketMqProducerProperties.getSendMsgTimeout());
transactionMQProducer.setRetryTimesWhenSendFailed(rocketMqProducerProperties.getRetryTimesWhenSendFailed());
// 这里就是差别
// 注册了一个事务监听器
transactionMQProducer.setTransactionListener(new TransactionListener() {
/**
* 发送半消息成功后,执行本地事务
*/
@Override
public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
System.out.println("执行本地事务");
return msg.getBody().length == 0 ? LocalTransactionState.ROLLBACK_MESSAGE : LocalTransactionState.COMMIT_MESSAGE;
}
/**
* 检查本地事务是否成功
*/
@Override
public LocalTransactionState checkLocalTransaction(MessageExt msg) {
System.out.println("超时询问");
return LocalTransactionState.ROLLBACK_MESSAGE;
}
});
try {
transactionMQProducer.start();
log.info("transaction producer starts successfully !, groupName: {} ,namesrvAddr:{}", rocketMqProducerProperties.getGroupName(), rocketMqProducerProperties.getNamesrvAddr());
} catch (MQClientException e) {
e.printStackTrace();
log.info("transaction producer starts failure !, groupName: {} ,namesrvAddr:{}", rocketMqProducerProperties.getGroupName(), rocketMqProducerProperties.getNamesrvAddr());
}
return transactionMQProducer;
}
面试题
1.RocketMQ发送事务消息的流程
生产者先向broker发送一条半消息,这个消息是对用户不可见的
broker收到半消息后会先将这条消息放到事务相关队列中
接着告诉生产者执行本地事务executeLocalTransaction
生产者执行本地事务成功则向broker发送commit消息让broker提交,将消息从事务消息队列中取出放到生产者指定的topic中
生产者执行本地事务失败则向broker发送rollback消息让broker回滚,将消息从事务消息队列中删除
由于生产者故障或者网络波动导致生产者的本地事务执行情况长时间没有通知broker
则broker会主动询问生产者本地事务执行情况checkLocalTransaction
如果是因为生产者故障,则broker会在失败一定次数后执行rollback操作
消费者一直监听topic
只有当事务消息被提交后,才会被放到生产者指定的topic中,这个时候消费者才会收到这条消息,执行本地的逻辑开始处理
2.RocketMQ的事务消息是如何保证数据的最终一致性的
RocketMQ的事务消息是分布式事务方案中使用可靠消息最终一致性
是BASE理论的体现,强调的是最终一致性
生产者这边先通过half消息和本地事务保证了数据的一致性
消费者不断从topic中取出消息去处理,如果处理失败就重试,重试次数过多就会告警交由人工处理
3.生产者本地事务执行成功,由于网络原因导致commit消息没有被broker接收到,broker回查事务的时候由于生产者宕机或者网络原因,多次尝试超时后回滚了事务消息,这种情况怎么办
这个问题是我想出来的,我不知道出现这种情况怎么办😭
大佬们会这道题的评论区留言