SpringBoot RabbitMQ ACK 机制

本文详细解析了SpringBoot集成RabbitMQ时的ACK机制,包括配置方法、消息生产端确认机制及消费端确认流程。通过具体代码示例,阐述了如何确保消息可靠传输,以及如何处理消息消费异常。

SpringBoot RabbitMQ ACK 机制

Rabbit ACK 机制流程图

在这里插入图片描述

实现步骤

application.yml 配置

spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
    # publisher-confirms和publisher-returns是对于消息生产端的配置
    publisher-confirms: true # 开启发送消息确认 对应RabbitTemplate.ConfirmCallback接口
    publisher-returns: true  # 开启发送消息失败返回 对应RabbitTemplate.ReturnCallback接口

    # 这个配置是针对消息消费端的配置
    listener:
      direct:
        acknowledge-mode: manual # 开启 ack 手动确认
      simple:
        acknowledge-mode: manual # 开启 ack 手动确认

消息生产端发送确认配置

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;

@Configuration
public class RabbitTemplateConfig implements RabbitTemplate.ConfirmCallback, RabbitTemplate.ReturnCallback{

    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @PostConstruct
    public void init(){
        rabbitTemplate.setConfirmCallback(this);            // 指定 ConfirmCallback
        rabbitTemplate.setReturnCallback(this);             // 指定 ReturnCallback
    }


    /**
     * 如果消息到达 exchange, 则 confirm 回调, ack = true
     * 如果消息不到达 exchange, 则 confirm 回调, ack = false
     * 需要设置spring.rabbitmq.publisher-confirms=true
     * @param correlationData
     * @param ack
     * @param cause
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {

        logger.info("消息是否到达Exchange:{}", ack == true ? "消息成功到达Exchange" : "消息到达Exchange失败");
        if (!ack) {
            logger.info("消息到达Exchange失败原因:{}", cause);

            // 根据业务逻辑实现消息补偿机制

        }

    }

    /**
     * exchange 到达 queue, 则 returnedMessage 不回调
     * exchange 到达 queue 失败, 则 returnedMessage 回调
     * 需要设置spring.rabbitmq.publisher-returns=true
     * @param message
     * @param replyCode
     * @param replyText
     * @param exchange
     * @param routingKey
     */
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        logger.info("消息报文:{}", new String(message.getBody()));
        logger.info("消息编号:{}", replyCode);
        logger.info("描述:{}", replyText);
        logger.info("交换机名称:{}", exchange);
        logger.info("路由名称:{}", routingKey);

        // 根据业务逻辑实现消息补偿机制

    }
}

消息消费关消费消息确认

@RabbitListener(queues = {Constant.QUEUE_NAME_TEST})
public void queueTestConsumer(RabbitObject rabbitObject, Channel channel, Message message){

    String msg = new String(message.getBody());

    try {

        // 当前线程休眠5秒钟,模拟消息消费过程
        Thread.sleep(5000);
        // 正常消费消息
        logger.info("-------- 消息-id:{}", rabbitObject.toString());

        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        logger.error("消息[{}]确认处理成功", msg);


    } catch (Exception e) {

        // redelivered = true, 表明该消息是重复处理消息
        Boolean redelivered = message.getMessageProperties().getRedelivered();

        /**
         * 这里对消息重入队列做设置,例如将消息序列化缓存至 Redis, 并记录重入队列次数
         * 如果该消息重入队列次数达到一次次数,比如3次,将不再重入队列,直接拒绝
         * 这时候需要对消息做补偿机制处理
         *
         * channel.basicNack与channel.basicReject要结合越来使用
         *
         */
        try {

            if (redelivered) {

                /**
                 * 1. 对于重复处理的队列消息做补偿机制处理
                 * 2. 从队列中移除该消息,防止队列阻塞
                 */
                // 消息已重复处理失败, 扔掉消息
                channel.basicReject(message.getMessageProperties().getDeliveryTag(), false); // 拒绝消息
                logger.error("消息[{}]重新处理失败,扔掉消息", msg);
            }

            // redelivered != true,表明该消息是第一次消费
            if (!redelivered) {

                // 消息重新放回队列
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
                logger.error("消息[{}]处理失败,重新放回队列", msg);
            }

        } catch (IOException e1) {
            e1.printStackTrace();
        }


        e.printStackTrace();
    }
}
  • channel.basicAck: 表示消息正常消费
  • channel.basicNack: 消息消费异常,将消息重新往往队列
  • channel.basicReject: 消息消费异常,拒绝该消息入队列

博客地址:
SpringBoot 企业级应用实战
基于 SpringBoot 集成 RabbitMQ
源码地址:
基于 SpringBoot 集成 RabbitMQ

### Spring Boot 中 RabbitMQ 的消息确认 (ACK) 机制 #### 配置 `application.yml` 文件中的 RabbitMQ 参数 为了启用消息的手动确认模式,在 `application.yml` 或 `application.properties` 文件中需指定相应的配置项。具体来说,通过设置 `acknowledge-mode: manual` 来开启消费端的手动应答功能[^4]。 ```yaml spring: rabbitmq: listener: simple: acknowledge-mode: manual # 设置为手动确认方式 ``` #### 创建监听器类并处理接收到的消息 当应用程序作为消费者接收到来自 RabbitMQ 的消息时,可以通过实现 `ChannelAwareMessageListener` 接口来定义具体的业务逻辑以及执行 ACK 操作的方法。下面是一个简单的例子: ```java import org.springframework.amqp.core.Message; import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener; import com.rabbitmq.client.Channel; public class MyMessageListener implements ChannelAwareMessageListener { @Override public void onMessage(Message message, Channel channel) throws Exception { try { String msg = new String(message.getBody()); System.out.println("Received message: " + msg); // 处理业务逻辑 // 手动确认消息已被成功处理 channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); } catch (Exception e) { // 如果发生异常,则拒绝该条消息,并决定是否重新入队 channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true); } } } ``` 在此代码片段中,每当有新消息到达时都会触发 `onMessage()` 方法;如果一切正常则调用 `basicAck()` 进行确认,反之则会调用 `basicNack()` 表明存在问题[^2]。 #### 发送测试消息验证效果 最后一步是在控制器或其他地方编写发送消息的功能以便于测试整个流程。可以创建一个 RESTful API 端点用于向队列推送数据,例如: ```java @RestController @RequestMapping("/send") public class MessageController { private final AmqpTemplate amqpTemplate; @Autowired public MessageController(AmqpTemplate amqpTemplate){ this.amqpTemplate = amqpTemplate; } @GetMapping public ResponseEntity<String> sendMessage(@RequestParam String message){ amqpTemplate.convertAndSend("exchangeName", "routingKey", message); return ResponseEntity.ok("Sent"); } } ``` 此时启动应用并通过浏览器或 Postman 访问 `/send?message=HelloRabbitMQ` 即可完成一次完整的交互过程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值