RabbitMQ投递消息时确认机制Confirm和Return

本文介绍了RabbitMQ的消息确认(Confirm)和返回(Return)机制。通过开启Confirm模式并设置确认回调,当消息成功到达交换机时会触发handleAck方法,未成功则触发handleNack。若消息无法正确路由到队列,需配合监听器和参数设置为true来处理非正确路由的情况,确保消息不会丢失。
<dependencies>
    <dependency>
        <groupId>com.rabbitmq</groupId>
        <artifactId>amqp-client</artifactId>
        <version>5.10.0</version>
    </dependency>
</dependencies>
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class SendMessage {

    private final static String QUEUE_NAME = "test";

    private final static String ROUTE_KEY = QUEUE_NAME;

    private final static String MESSAGE = "I love three things in this world,the sun ,the moon and you.";

    public static void main(String[] args) throws IOException, TimeoutException {
        ConnectionFactory factory = new ConnectionFactory();
        {
            factory.setHost("49.232.202.201");
            factory.setUsername("gosuncn");
            factory.setPassword("123456");
            factory.setVirtualHost("/gosuncn");
        }
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        /**
         * 消息确认模式[confirm]
         */
        channel.confirmSelect();
        /**
         * 添加确认监听器
         */
        channel.addConfirmListener(new ConfirmListener() {
            //只要是交换机确认收到了消息就会回调ConfirmListener的handleAck方法。(即使交换机没有正确路由到队列中)
            @Override
            public void handleAck(long deliveryTag, boolean multiple) throws IOException {
                if (multiple) {
                    System.out.println("Before No." + deliveryTag + " have been confirmed.");
                } else {
                    System.out.println("No." + deliveryTag + " have been confirmed.");
                }
            }
            //只要是交换机没有确认收到了消息就会回调ConfirmListener的handleNack方法。
            @Override
            public void handleNack(long deliveryTag, boolean multiple) throws IOException {
                if (multiple) {
                    System.out.println("Before No." + deliveryTag + " have been not confirmed.");
                } else {
                    System.out.println("No." + deliveryTag + " have been not confirmed.");
                }
            }
        });
        /**
         * 如果消息到达了交换机且路由不可达那么回调ReturnListener的方法。
         * void basicPublish(String exchange, String routingKey, boolean mandatory, BasicProperties props, byte[] body)
         * 生效条件:第三个参数 mandatory 需要设置为true。
         */
        channel.addReturnListener((replyCode, replyText, exchange, routingKey, properties, body) -> {
            System.out.println(replyCode);
            System.out.println(replyText);
            System.out.println(exchange);
            System.out.println(routingKey);
        });
        /**
         * 发布消息
         */
        channel.basicPublish("", ROUTE_KEY, true, null, MESSAGE.getBytes());
        System.out.println("message delivery [" + MESSAGE + "]");
    }

}

第1步:开启confirm模式

channel.confirmSelect();

第2步:添加确认回调

void addConfirmListener(ConfirmListener listener);

public interface ConfirmListener {
    void handleAck(long deliveryTag, boolean multiple) throws IOException;

    void handleNack(long deliveryTag, boolean multiple) throws IOException;
}

需要重写两个方法handleAckhandleNack

这样,已经可以实现Confirm的回调了。那么到底是什么时机执行回调呢?

handleAck方法:消息被basicPublish到RabbitMQ的交换机上,一旦确认收到消息,就会回调。

handleNack方法:与之相反,没有确认收到消息,就会回调。

如果消息被送达交换机,但是交换机没有正确的路由到队列中,该怎么办呢?

只有队列可以存储消息,交换机并不能,如果交换机没有正确的路由到队列中,消息可能丢失。

这时,我们需要使用Return监听器,来回调非正确路由的业务逻辑。

void addReturnListener(ReturnListener listener);

但是,要想Return监听器生效,需要将

void basicPublish(String exchange, String routingKey, boolean mandatory, BasicProperties props, byte[] body)

第三个参数 mandatory 设置为true,才行。

<think>好的,我现在需要帮助用户理解RabbitMQ的生产者消息确认机制,也就是Publisher Confirms。用户已经提供了三个站内引用,我需要结合这些引用以及之前的回答来给出详细的解释。首先,我得回顾一下用户之前的问题我的回答,确保一致性。 用户之前问的是X-Forwarded-ForX-Real-IP的区别,我的回答结构比较清晰,分点说明设计目的、可信度、使用场景等,并附带了代码示例总结表格。现在用户转向了RabbitMQ消息确认机制,可能需要类似的结构化回答,分点说明工作原理、实现方式、最佳实践等。 首先,根据引用[2],Publisher Confirms的基本思想是生产者发送消息RabbitMQ,当broker确认接收后,发送ACK给生产者,否则可能需要重发。引用[3]提到confirm模式只能保证消息到达broker,不能保证投递队列,因此需要结合return模式来处理无法路由的消息。引用[1]提到三种策略:单独发布、批量发布异步处理,以及如何追踪未确认消息重新发布。 我需要整合这些信息,解释确认机制的工作流程,三种策略的区别,如何确保消息不丢失,以及结合return模式的使用。同,用户可能想知道最佳实践,比如如何配置、处理未确认消息、异步监听等。 还要注意用户可能的应用场景,比如在高并发环境下如何选择策略,如何处理消息丢失的情况,以及配置上的注意事项。需要确保回答清晰,步骤明确,可能还需要代码示例,比如Spring Boot的配置,或者Python的示例,但用户之前的例子用了Python,可能需要保持一致。 另外,用户可能对confirm模式return模式的关系有疑问,需要解释清楚两者的不同作用,confirm是broker确认接收,return是处理无法路由到队列的情况。同,最佳实践中要提到两者的结合使用,以及如何配置参数如spring.rabbitmq.publisher-returnsmandatory属性。 还要考虑安全性,比如异步确认的回调处理,如何记录日志或重发机制。可能还需要提到性能优化,比如批量确认异步确认对吞吐量的影响,帮助用户根据场景选择合适的策略。 总结一下,回答的结构应该是: 1. 工作原理:分步骤说明消息发送到确认的过程。 2. 三种确认策略:单独、批量、异步,各自优缺点。 3. 结合return模式,确保消息路由到队列。 4. 实现方式:代码配置示例,比如Spring Boot或Python。 5. 最佳实践:包括配置建议、异常处理、性能优化等。 6. 相关问题:生成相关的问题,引导用户深入理解。 需要确保引用正确,比如引用[3]提到配置mandatorypublisher-returns,需要正确引用。同,注意用户可能混淆confirm事务,可能需要简单区分,但根据引用,用户主要关注confirm机制,所以可以略过事务部分。 最后,检查是否有遗漏的关键点,比如消息持久化,虽然引用中没有提到,但作为最佳实践的一部分,可能需要提到消息队列的持久化设置,以配合确认机制确保消息不丢失。</think>### RabbitMQ 生产者消息确认机制(Publisher Confirms)详解 --- #### 一、**工作原理** RabbitMQ 生产者消息确认机制(Publisher Confirms)通过以下流程确保消息可靠投递: 1. **消息发送**:生产者将消息发送到 RabbitMQ Broker。 2. **Broker 确认**: - 若 Broker **成功接收消息**(写入磁盘或内存),则返回 **ACK(确认标识)** 给生产者。 - 若 Broker **因故障丢失消息**(如节点宕机),则返回 **NACK(未确认标识)** 或超无响应[^2][^3]。 3. **生产者处理**: - 收到 ACK:标记消息发送成功,无需重试。 - 收到 NACK 或超:触发重发机制,确保消息不丢失。 **核心目标**:解决网络波动或 Broker 故障导致的消息丢失问题。 --- #### 二、**三种确认策略** | 策略 | 实现方式 | 适用场景 | 优缺点 | |-----------------|--------------------------------------------------------------------------|------------------------------|-----------------------------------| | **单独确认** | 每发送一条消息后同步等待 ACK | 低吞吐量场景(如测试环境) | 可靠性高,但性能差(吞吐量低) | | **批量确认** | 累积多条消息后批量发送,统一等待 ACK | 中等吞吐量场景 | 性能提升,但单条失败需重发整个批次 | | **异步确认** | 通过回调函数异步处理 ACK/NACK,无需阻塞生产者线程 | 高并发场景(生产环境首选) | 性能最优,需处理并发消息追踪逻辑 | **代码示例(异步确认)**: ```python # Python pika 库示例 channel.confirm_delivery() # 开启确认模式 def on_ack(frame): print(f"消息 {frame.method.delivery_tag} 已确认") def on_nack(frame): print(f"消息 {frame.method.delivery_tag} 未确认,需重发") channel.add_callback(on_ack, callback_args=[channel.CallbackType.ON_ACK]) channel.add_callback(on_nack, callback_args=[channel.CallbackType.ON_NACK]) ``` --- #### 三、**结合 Return 模式确保路由可靠性** Confirm 模式仅保证消息到达 Broker,但无法确保消息被正确路由到队列。需搭配 **Return 模式**: 1. **配置参数**(以 Spring Boot 为例): ```yaml spring.rabbitmq: publisher-returns: true # 开启 Return 回调 template.mandatory: true # 强制消息必须路由到队列 ``` 2. **处理未被路由的消息**: - 若消息因路由键错误或队列不存在无法投递RabbitMQ 会通过 `ReturnCallback` 通知生产者[^3]。 - 生产者需记录日志或触发补偿机制(如重试或告警)。 --- #### 四、**最佳实践** 1. **异步确认 + 消息追踪**: - 使用唯一标识(如 `messageId`)标记每条消息,在回调中维护发送状态(如内存缓存或数据库)。 - 定检查未确认消息,触发重发(需避免无限重试)。 2. **幂等性设计**: - 消费者端需处理重复消息(如通过业务唯一键去重)。 3. **持久化配置**: - 消息设置持久化属性(`delivery_mode=2`),队列声明为持久化(`durable=True`),防止 Broker 重启丢失数据。 4. **性能优化**: - 调整 `confirm.select` 的批量提交阈值(如每 100 条触发一次 ACK 等待)。 - 避免在异步回调中执行阻塞操作(如同步数据库写入)。 --- #### 五、**关键问题与解决方案** | 问题 | 解决方案 | |--------------------------|--------------------------------------------------------------------------| | **如何追踪未确认消息?** | 维护消息状态表,记录发送间、重试次数,超后触发重试或告警[^1]。 | | **NACK 后如何重发?** | 使用延迟队列或定任务重新投递,限制最大重试次数(如 3 次)。 | | **高并发下性能瓶颈?** | 采用异步确认 + 非阻塞 IO,结合线程池处理回调逻辑。 | --- ### 相关问题 1. Confirm 模式与事务(Transaction)机制有何区别? 2. 如何处理 RabbitMQ 集群环境下的消息确认? 3. 如何设计消息重试策略以避免雪崩效应? --- ### 引用说明 [^1]: RabbitMQ 发布确认模式需结合消息状态追踪实现可靠性。 [^2]: Confirm 模式的核心是通过 ACK/NACK 机制实现生产者与 Broker 的可靠通信。 [^3]: Return 模式补充了 Confirm 机制的不足,确保消息正确路由到队列
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值