什么是RocketMQ事务消息?
在分布式系统中,确保数据的一致性是一个关键挑战。尤其是在涉及多个数据库或服务的情况下,保证所有操作要么全部成功要么全部失败(即原子性)尤为重要。事务消息是一种解决这一问题的有效方法,RocketMQ作为一种高性能、高可用的消息中间件,提供了对事务消息的支持。
RocketMQ事务消息的工作原理
RocketMQ事务消息的工作流程可以分为三个阶段:
- 发送事务消息的半消息阶段(Prepare):生产者首先向Broker发送一条半消息,Broker接收到消息后会持久化并返回确认给生产者。此时消息对消费者不可见。
- 本地事务执行阶段:生产者根据半消息执行本地事务逻辑,并根据事务执行结果向Broker提交事务状态(提交或回滚)。
- 事务回查阶段:如果Broker收到事务状态为RocketMQLocalTransactionState.UNKNOWN
,它会向生产者发起事务回查,生产者需要根据本地事务的状态返回最终的提交或回滚结果。
RocketMQ的分布式事务:两阶段提交+消费者尽最大可能+失败补偿(死信消息)。
RocketMQ事务消息的实现
下面,我们通过一个实际的例子来演示如何在Spring Boot项目中实现RocketMQ事务消息。
准备工作
首先,在你的Spring Boot项目中添加RocketMQ的依赖:
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
在application.yml
或application.properties
中配置RocketMQ:
rocketmq:
name-server: 192.168.110.236:9876
producer:
group: producer-group
consumer:
group: consumer-group
创建生产者发送事务消息
生产者类负责发送事务消息,并实现消息发送后的事务逻辑:
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.apache.rocketmq.spring.support.RocketMQHeaders;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.UUID;
@Slf4j
@Service
public class ProducerTransaction {
@Resource
private RocketMQTemplate rocketMQTemplate;
public void send(String msg) {
// 构建消息
String transactionId = UUID.randomUUID().toString();
Message<String> message = MessageBuilder.withPayload(msg)
.setHeader(RocketMQHeaders.TRANSACTION_ID, transactionId)
.build();
// 发送事务消息
rocketMQTemplate.sendMessageInTransaction("transaction-topic", message, "自定义参数");
log.info("send message: {}", msg);
}
}
实现本地事务逻辑
事务监听器类负责执行本地事务和事务回查逻辑:
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionListener;
import org.apache.rocketmq.spring.core.RocketMQLocalTransactionState;
import org.springframework.messaging.Message;
/**
* @author
*/
@Slf4j
@RocketMQTransactionListener
public class ProductTransactionListener implements RocketMQLocalTransactionListener {
@Override
public RocketMQLocalTransactionState executeLocalTransaction(Message msg, Object arg) {
log.info("在这执行本地事务 executeLocalTransaction:{},{}", msg, arg);
try {
// 执行本地事务操作
return RocketMQLocalTransactionState.COMMIT;
} catch (Exception e) {
log.error("executeLocalTransaction error", e);
return RocketMQLocalTransactionState.ROLLBACK;
}
}
/**
* 如果提交 UNKNOWN 状态,RocketMQ 会定期回查本地事务状态状态,则会调用此方法
*
* @param msg
* @return
*/
@Override
public RocketMQLocalTransactionState checkLocalTransaction(Message msg) {
try {
log.info("回查checkLocalTransaction:{}", msg);
return RocketMQLocalTransactionState.COMMIT;
} catch (Exception e) {
return RocketMQLocalTransactionState.ROLLBACK;
}
}
}
创建消费者处理事务消息
消费者类负责接收和处理事务消息:
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;
@Slf4j
@Component
@RocketMQMessageListener(topic = "transaction-topic", consumerGroup = "transaction-group",
//最大重试次数
maxReconsumeTimes = 2)
public class ConsumerTransaction implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
log.info("事务消息 消费者接收到消息:{}", message);
//模拟消息处理失败 会触发重试 重试次数达到 会进入死信队列
throw new RuntimeException();
}
}
达到设置重试次数,会进入死信队列:
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;
/**
* 死信队列
*/
@Slf4j
@Component
@RocketMQMessageListener(topic = "%DLQ%transaction-group", consumerGroup = "%DLQ%transaction-group")
public class DLQConsumerTransaction implements RocketMQListener<String> {
@Override
public void onMessage(String message) {
//可以做事务的回滚逻辑,保证最终一致性
log.info("死信 消费者接收到消息:{}", message);
}
}
事务消息的最佳实践
在实际应用中,事务消息的使用需要考虑到多种因素,以确保系统的可靠性和一致性。以下是一些最佳实践:
- 消息的幂等性:确保每条消息的处理是幂等的,即同一条消息被多次处理不会影响系统的最终结果。这可以通过业务逻辑设计或数据库唯一约束来实现。
- 本地事务的执行和回查:本地事务的执行逻辑应尽量简洁,减少复杂操作,降低出错的风险。同时,事务消息的回查逻辑应根据具体业务场景实现,确保能够准确判断本地事务的状态。
- 超时设置和重试机制:合理设置事务消息的超时时间,避免消息长时间处于未决状态。在本地事务执行失败或回查失败时,实现合理的重试机制,确保消息最终状态的一致性。
- 性能优化:在可能的情况下,使用批量处理消息以提高系统的吞吐量和性能。合理配置RocketMQ的生产者和消费者资源,避免资源瓶颈。
- 监控和报警:使用RocketMQ自带的监控工具或其他第三方监控工具,实时监控消息的生产和消费情况。设置合理的报警机制,及时发现和处理异常情况,如消息堆积、消息处理失败等。
结论
通过本文的介绍,我们了解了RocketMQ事务消息的基本概念和实现方法。在分布式系统中,合理使用事务消息可以有效保证数据的一致性和可靠性,是构建高可用、高可靠系统的重要工具。
希望这篇博客能帮助你更好地理解和使用RocketMQ事务消息。如果你在实际应用中遇到问题或有其他疑问,欢迎在评论区留言,我们将共同探讨解决方案。