RocketMQ实现分布式事务

什么是RocketMQ事务消息?

在分布式系统中,确保数据的一致性是一个关键挑战。尤其是在涉及多个数据库或服务的情况下,保证所有操作要么全部成功要么全部失败(即原子性)尤为重要。事务消息是一种解决这一问题的有效方法,RocketMQ作为一种高性能、高可用的消息中间件,提供了对事务消息的支持。

RocketMQ事务消息的工作原理

RocketMQ事务消息的工作流程可以分为三个阶段:

  1. 发送事务消息的半消息阶段(Prepare):生产者首先向Broker发送一条半消息,Broker接收到消息后会持久化并返回确认给生产者。此时消息对消费者不可见。
  2. 本地事务执行阶段:生产者根据半消息执行本地事务逻辑,并根据事务执行结果向Broker提交事务状态(提交或回滚)。
  3. 事务回查阶段:如果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.ymlapplication.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);
    }
}

事务消息的最佳实践

在实际应用中,事务消息的使用需要考虑到多种因素,以确保系统的可靠性和一致性。以下是一些最佳实践:

  1. 消息的幂等性:确保每条消息的处理是幂等的,即同一条消息被多次处理不会影响系统的最终结果。这可以通过业务逻辑设计或数据库唯一约束来实现。
  2. 本地事务的执行和回查:本地事务的执行逻辑应尽量简洁,减少复杂操作,降低出错的风险。同时,事务消息的回查逻辑应根据具体业务场景实现,确保能够准确判断本地事务的状态。
  3. 超时设置和重试机制:合理设置事务消息的超时时间,避免消息长时间处于未决状态。在本地事务执行失败或回查失败时,实现合理的重试机制,确保消息最终状态的一致性。
  4. 性能优化:在可能的情况下,使用批量处理消息以提高系统的吞吐量和性能。合理配置RocketMQ的生产者和消费者资源,避免资源瓶颈。
  5. 监控和报警:使用RocketMQ自带的监控工具或其他第三方监控工具,实时监控消息的生产和消费情况。设置合理的报警机制,及时发现和处理异常情况,如消息堆积、消息处理失败等。

结论

通过本文的介绍,我们了解了RocketMQ事务消息的基本概念和实现方法。在分布式系统中,合理使用事务消息可以有效保证数据的一致性和可靠性,是构建高可用、高可靠系统的重要工具。

希望这篇博客能帮助你更好地理解和使用RocketMQ事务消息。如果你在实际应用中遇到问题或有其他疑问,欢迎在评论区留言,我们将共同探讨解决方案。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值