RabbitMQ断链重连的配置

本文介绍如何在RabbitMQ中为生产者消费者连接和RabbitAdmin连接设置不同策略,确保网络恢复后自动创建并绑定队列。通过实例展示了在CalcRabbitMQConfig中创建两个连接工厂,一个用于RabbitAdmin,一个用于实际通信,从而解决自动创建队列的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    • 问题描述:

自动删除的队列在网络断开恢复后不能自动创建并绑定关系。

    • 解决办法:

在RabbitMQConfig中分别声明两个连接工厂类,其中一个用于RabbitAdmin声明的连接,另一个用于生产者和消费者的连接。其中用于RabbitAdmin声明的连接不设置连接、通道、恢复的事件监听,用于生产者和消费者的连接需要设置连接、通道、恢复的事件监听,并且在通道监听事件“onCreate(Channel channel, boolean b)”方法中调用声明队列和绑定关系。

    • 参考示例:

CalcRabbitMQConfig

package com.cdjdgm.calc.config.rabbitmq;

import com.cdjdgm.calc.component.rabbitmq.CalcRabbitEventListener;

import com.cdjdgm.calc.component.rabbitmq.CalcRabbitMessageConverter;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.amqp.rabbit.annotation.EnableRabbit;

import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;

import org.springframework.amqp.rabbit.connection.ConnectionFactory;

import org.springframework.amqp.rabbit.core.RabbitAdmin;

import org.springframework.amqp.rabbit.core.RabbitTemplate;

import org.springframework.beans.factory.annotation.Qualifier;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.context.annotation.Primary;

import org.springframework.scheduling.concurrent.CustomizableThreadFactory;

import java.util.concurrent.atomic.AtomicInteger;

/**

 * RabbitMQ配置类

 *

 * @author ZhaoYong

 */

@Configuration

@EnableRabbit

@ConditionalOnBean(name = "calcRabbitMQProperties")

public class CalcRabbitMQConfig {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private final String prefix = "calc";

    private final AtomicInteger connectionNameStrategyCounter1 = new AtomicInteger();

    private final AtomicInteger connectionNameStrategyCounter2 = new AtomicInteger();

    @Primary

    @Bean(name = "calcRabbitConnectionFactory")

    public CachingConnectionFactory calcRabbitConnectionFactory(CalcRabbitMQProperties calcRabbitMQProperties,//

                                                                CalcRabbitEventListener calcRabbitEventListener) {

        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();

        if (calcRabbitMQProperties.getHost().contains(":")) {

            connectionFactory.setAddresses(calcRabbitMQProperties.getHost());

        } else {

            connectionFactory.setHost(calcRabbitMQProperties.getHost());

            connectionFactory.setPort(calcRabbitMQProperties.getPort());

        }

        connectionFactory.setUsername(calcRabbitMQProperties.getUsername());

        connectionFactory.setPassword(calcRabbitMQProperties.getPassword());

        connectionFactory.setVirtualHost(calcRabbitMQProperties.getVirtualHost());

        connectionFactory.setPublisherReturns(calcRabbitMQProperties.getPublisherReturns());

        connectionFactory.setPublisherConfirmType(calcRabbitMQProperties.getPublisherConfirmType());

        connectionFactory.getRabbitConnectionFactory().setAutomaticRecoveryEnabled(true);

        connectionFactory.getRabbitConnectionFactory().setTopologyRecoveryEnabled(true);

        connectionFactory.getRabbitConnectionFactory().setNetworkRecoveryInterval(5000L);

        connectionFactory.setConnectionThreadFactory(new CustomizableThreadFactory("calc-rabbitmq-"));

        connectionFactory.addConnectionListener(calcRabbitEventListener);

        connectionFactory.addChannelListener(calcRabbitEventListener);

        connectionFactory.setRecoveryListener(calcRabbitEventListener);

        connectionFactory.setConnectionNameStrategy(connectionFactory1 -> "[" + prefix + "]ConnectionFactory" + //

                ":" + connectionNameStrategyCounter1.getAndIncrement());

        return connectionFactory;

    }

    @Bean(name = "calcRabbitConnectionFactoryAdmin")

    public CachingConnectionFactory calcRabbitConnectionFactoryAdmin(CalcRabbitMQProperties calcRabbitMQProperties) {

        CachingConnectionFactory connectionFactory = new CachingConnectionFactory();

        if (calcRabbitMQProperties.getHost().contains(":")) {

            connectionFactory.setAddresses(calcRabbitMQProperties.getHost());

        } else {

            connectionFactory.setHost(calcRabbitMQProperties.getHost());

            connectionFactory.setPort(calcRabbitMQProperties.getPort());

        }

        connectionFactory.setUsername(calcRabbitMQProperties.getUsername());

        connectionFactory.setPassword(calcRabbitMQProperties.getPassword());

        connectionFactory.setVirtualHost(calcRabbitMQProperties.getVirtualHost());

        connectionFactory.setPublisherReturns(calcRabbitMQProperties.getPublisherReturns());

        connectionFactory.setPublisherConfirmType(calcRabbitMQProperties.getPublisherConfirmType());

        connectionFactory.getRabbitConnectionFactory().setAutomaticRecoveryEnabled(true);

        connectionFactory.getRabbitConnectionFactory().setTopologyRecoveryEnabled(true);

        connectionFactory.getRabbitConnectionFactory().setNetworkRecoveryInterval(5000L);

        connectionFactory.setConnectionThreadFactory(new CustomizableThreadFactory("calc-rabbitmq-admin-"));

        connectionFactory.setConnectionNameStrategy(connectionFactory2 -> "[" + prefix + "]ConnectionFactoryAdmin" + //

                ":" + connectionNameStrategyCounter2.getAndIncrement());

        return connectionFactory;

    }

    @Primary

    @Bean(name = "calcRabbitAdmin")

    public RabbitAdmin calcRabbitAdmin(@Qualifier("calcRabbitConnectionFactoryAdmin") ConnectionFactory connectionFactory) {

        RabbitAdmin rabbitAdmin = new RabbitAdmin(connectionFactory);

        rabbitAdmin.setAutoStartup(true);

        return rabbitAdmin;

    }

    @Primary

    @Bean(name = "calcRabbitTemplate")

    public RabbitTemplate calcRabbitTemplate(@Qualifier("calcRabbitConnectionFactory") ConnectionFactory connectionFactory,//

                                             @Value("${spring.rabbitmq.calc.template.mandatory:true}") Boolean mandatory,//

                                             CalcRabbitMessageConverter messageConverter) {

        RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);

        rabbitTemplate.setEncoding("UTF-8");

        rabbitTemplate.setMandatory(mandatory);

        rabbitTemplate.setMessageConverter(messageConverter);

        return rabbitTemplate;

    }

    // @Primary

    // @Bean(name = "calcRabbitContainerFactory")

    // public SimpleRabbitListenerContainerFactory calcRabbitContainerFactory(@Qualifier("calcRabbitConnectionFactory") ConnectionFactory connectionFactory,//

    //                                                                        CalcRabbitMessageConverter messageConverter) {

    //     SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();

    //     factory.setConnectionFactory(connectionFactory);

    //     factory.setMessageConverter(messageConverter);

    //     factory.setAcknowledgeMode(AcknowledgeMode.NONE);

    //     return factory;

    // }

}

CalcRabbitComponent

package com.cdjdgm.calc.component.rabbitmq;

import com.cdjdgm.calc.config.common.CalcConstants;

import com.cdjdgm.calc.config.rabbitmq.CalcRabbitMQConfig;

import com.cdjdgm.calc.config.rabbitmq.CalcRabbitMQProperties;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.amqp.core.AcknowledgeMode;

import org.springframework.amqp.core.BindingBuilder;

import org.springframework.amqp.core.Queue;

import org.springframework.amqp.core.TopicExchange;

import org.springframework.amqp.rabbit.connection.ConnectionFactory;

import org.springframework.amqp.rabbit.core.RabbitAdmin;

import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;

import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;

import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

import javax.annotation.PreDestroy;

import javax.annotation.Resource;

import java.util.HashMap;

import java.util.Map;

import java.util.Random;

/**

 * MQ对象初始化

 */

@Component

@ConditionalOnBean(CalcRabbitMQConfig.class)

public class CalcRabbitComponent {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    @Resource(name = "calcRabbitMQProperties")

    private CalcRabbitMQProperties calcRabbitMQProperties;

    @Resource(name = "calcRabbitConnectionFactory")

    private ConnectionFactory connectionFactory;

    @Resource(name = "calcRabbitAdmin")

    private RabbitAdmin calcRabbitAdmin;

    @Resource(name = "calcRabbitMessageListener")

    private CalcRabbitMessageListener calcRabbitMessageListener;

    private TopicExchange dcExchange = null;

    private TopicExchange healthExchange = null;

    private TopicExchange lifeExchange = null;

    private Queue dcQueue = null;

    private String dcQueueName = "";

    private SimpleMessageListenerContainer container = null;

    @PostConstruct

    public void init() {

        logger.info("Init rabbitmq exchange and queue.");

        // 随机生成队列名称

        dcQueueName = calcRabbitMQProperties.getQueueNameDcEquipData() + CalcConstants.HOST_NAME + "_" + //

                CalcConstants.HOST_ADDRESS + "_" + (new Random().nextInt(999));

        // 声明DC交换机

        dcExchange = new TopicExchange(calcRabbitMQProperties.getExchangeDcEquipData(), false, false);

        calcRabbitAdmin.declareExchange(dcExchange);// 申明交换机

        // 声明HEALTH交换机

        healthExchange = new TopicExchange(calcRabbitMQProperties.getExchangeCalcHealthData(), true, false);

        calcRabbitAdmin.declareExchange(healthExchange);// 申明交换机

        // 声明LIFE交换机

        lifeExchange = new TopicExchange(calcRabbitMQProperties.getExchangeCalcLifeData(), true, false);

        calcRabbitAdmin.declareExchange(lifeExchange);// 申明交换机

        // 声明绑定MQ的交换机和队列(此处不声明,在CalcRabbitEventListener中创建Channel成功后再申明)

        // declareQueueAndBinding();

        logger.info("Init rabbitmq message listener.");

        // 手动注册消费监听器

        container = new SimpleMessageListenerContainer(connectionFactory);

        // 设置消费线程数

        container.setConcurrentConsumers(1);

        container.setMaxConcurrentConsumers(1);

        container.setExposeListenerChannel(true);

        container.setAcknowledgeMode(AcknowledgeMode.MANUAL);

        // 设置消息监听器

        container.setMessageListener(calcRabbitMessageListener);

        // 添加关注的队列

        container.addQueueNames(dcQueueName);

        // 启动监听器(此处不启动,在CalcApplicationRunner中由应用启动完成后再统一启动)

        // container.start();

    }

    @PreDestroy

    public void destroy() {

        if (container != null && container.isRunning()) {

            logger.info("Destroy rabbitmq message listener.");

            container.stop();

        }

    }

    /**

     * 申明队列并绑定关系

     */

    public void declareQueueAndBinding() {

        // 声明从DC接收数据的交换机和队列并循环绑定

        logger.info("Declare queue [{}] and binding.", dcQueueName);

        Map<String, Object> args = new HashMap<>(2);

        args.put("x-max-length", calcRabbitMQProperties.getQueueMaxLengthDcEquipData());

        dcQueue = new Queue(dcQueueName, false, false, true, args);

        calcRabbitAdmin.declareQueue(dcQueue); // 申明队列

        String[] dcEquipRouteKeys = calcRabbitMQProperties.getRouteKeysDcEquipData();

        if (dcEquipRouteKeys != null && dcEquipRouteKeys.length > 0) {

            for (String dcEquipRouteKey : dcEquipRouteKeys) {

                calcRabbitAdmin.declareBinding(BindingBuilder.bind(dcQueue).to(dcExchange).with(dcEquipRouteKey)); // 申明绑定关系

            }

        }

    }

    /**

     * 手动启动MQ监听

     */

    public void startMessageListenerContainer() {

        if (container != null && !container.isRunning()) {

            logger.info("Start rabbitmq message listener.");

            container.start();

        }

    }

    /**

     * 手动停止MQ监听

     */

    public void stopMessageListenerContainer() {

        if (container != null && container.isRunning()) {

            logger.info("Stop rabbitmq message listener.");

            container.stop();

        }

    }

}

CalcRabbitEventListener

package com.cdjdgm.calc.component.rabbitmq;

import com.cdjdgm.calc.config.rabbitmq.CalcRabbitMQConfig;

import com.rabbitmq.client.Channel;

import com.rabbitmq.client.Recoverable;

import com.rabbitmq.client.RecoveryListener;

import com.rabbitmq.client.ShutdownSignalException;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.amqp.rabbit.connection.ChannelListener;

import org.springframework.amqp.rabbit.connection.Connection;

import org.springframework.amqp.rabbit.connection.ConnectionListener;

import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;

import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**

 * MQ事件监听器

 */

@Component

@ConditionalOnBean(CalcRabbitMQConfig.class)

public class CalcRabbitEventListener implements ConnectionListener, ChannelListener, RecoveryListener {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private final String prefix = "calc";

    @Resource

    private CalcRabbitComponent calcRabbitComponent;

    @Override

    public void onCreate(Connection connection) {

        logger.info("[{}]Connection created: [{}]", prefix, connection.toString());

    }

    @Override

    public void onClose(Connection connection) {

        logger.info("[{}]Connection closed: [{}]", prefix, connection.toString());

    }

    @Override

    public void onCreate(Channel channel, boolean b) {

        logger.info("[{}]Channel created: [{}]", prefix, channel.getChannelNumber());

        // 声明队列并绑定关系

        calcRabbitComponent.declareQueueAndBinding();

    }

    @Override

    public void onShutDown(ShutdownSignalException signal) {

        logger.error("[{}]Connection shutdowned: [{}]", prefix, signal.getMessage());

    }

    @Override

    public void onFailed(Exception exception) {

        logger.error("[{}]Connection failed: [{}]", prefix, exception.getMessage());

    }

    @Override

    public void handleRecovery(Recoverable recoverable) {

        logger.info("[{}]Connection recovery complete: [{}]", prefix, recoverable);

    }

    @Override

    public void handleRecoveryStarted(Recoverable recoverable) {

        logger.info("[{}]Connection recovery started: [{}]", prefix, recoverable);

    }

    @Override

    public void handleTopologyRecoveryStarted(Recoverable recoverable) {

        logger.info("[{}]Connection topology recovery started: [{}]", prefix, recoverable);

    }

}

<think>好的,用户现在问的是如何保证RabbitMQ消息的可靠传输。之前他们问过如何避免复消费,而这次的问题更侧于消息传输的可靠性。我需要回忆一下之前回答的结构,确保这次回答同样详细且有结构。 首先,我需要确定用户可能的使用场景。他们可能在开发一个分布式系统,使用RabbitMQ作为消息中间件。可靠传输是确保消息从生产者到消费者都不丢失的关键,特别是在金融、电商等对数据一致性要求高的领域。用户可能是开发者或架构师,需要确保系统的高可靠性。 接下来,用户的需求是什么?他们想知道如何配置RabbitMQ来防止消息丢失,可能涉及生产者的确认机制、队列持久化、消费者的手动确认等。用户可能已经遇到消息丢失的问题,或者正在设计系统,希望提前规避风险。 深层需求方面,用户可能希望了解全面的解决方案,包括生产端、Broker端和消费端的策略,以及监控和灾备措施。他们可能还需要实际配置示例,比如代码片段或配置参数,以便直接应用。 回想之前的回答结构,用户可能习惯分点说明,每个部分有原理和实现方式。我需要保持类似的格式,确保清晰易读。同时,可能需要对比不同确认模式(如事务、Confirm模式)的优缺点,帮助用户选择适合的方案。 需要注意RabbitMQ的特性,比如持久化需要同时设置队列和消息的属性,手动ACK的要性,以及如何处理网络故障或服务器崩溃的情况。此外,高可用性方面,镜像队列和集群部署可能也是用户关心的点。 在组织内容时,可以分三个主要部分:生产者、Broker、消费者,每个部分详细说明如何确保可靠传输。最后补充高可用和监控措施,确保全面覆盖。 可能用户对某些术语不熟悉,比如Publisher Confirm,需要简要解释。同时,给出配置示例或代码片段会更有帮助,比如JavaSpring Boot的配置,但用户可能使用不同的语言,所以保持示例通用或注明语言。 还要注意之前回答中的注意事项部分,比如性能权衡,确保用户了解可靠性和性能之间的平衡,比如持久化会增加开销,手动ACK可能影响吞吐量,但这是必要的代价。 最后,确保回答结构清晰,点突出,使用用户容易理解的语言,避免过于技术化但又不失专业性。可能还需要提及一些常见错误,比如忘记设置持久化或错误处理ACK,导致消息丢失的情况。</think>在 RabbitMQ 中保证消息的可靠传输,需要从 **生产者、Broker、消费者** 三个环节协同设计,以下是核心方案: --- ### **一、生产者端:确保消息成功投递** #### 1. **事务机制(不推荐高性能场景)** ```java channel.txSelect(); // 开启事务 try { channel.basicPublish(exchange, routingKey, props, body); channel.txCommit(); // 提交事务 } catch (Exception e) { channel.txRollback(); // 回滚 } ``` - **缺点**:同步阻塞,吞吐量下降约 2~10 倍。 #### 2. **Publisher Confirm 模式(推荐)** - **异步确认**:生产者发送消息后,Broker 异步返回确认信号(ACK/NACK)。 - **实现方式**: ```java channel.confirmSelect(); // 开启 Confirm 模式 channel.addConfirmListener((sequenceNumber, multiple) -> { // ACK 回调 // 消息成功持久化到磁盘(若配置) }, (sequenceNumber, multiple) -> { // NACK 回调 // 消息投递失败,需试或记录 }); ``` - **优势**:非阻塞,支持批量确认(`multiple=true`)。 --- ### **二、Broker 端:防止消息丢失** #### 1. **队列持久化** ```java // 声明队列时设置 durable=true channel.queueDeclare("order_queue", true, false, false, null); ``` - **要求**:队列元数据(名称、属性)持久化到磁盘。 #### 2. **消息持久化** ```java AMQP.BasicProperties props = new AMQP.BasicProperties.Builder() .deliveryMode(2) // 1=非持久,2=持久 .build(); channel.basicPublish(exchange, routingKey, props, body); ``` - **注意**:需同时设置队列和消息为持久化,否则启后消息仍可能丢失。 #### 3. **镜像队列(高可用)** ```shell # 配置策略,镜像到所有节点 rabbitmqctl set_policy ha-all "^ha\." '{"ha-mode":"all"}' ``` - **作用**:消息在集群多个节点间复制,避免单点故障。 --- ### **三、消费者端:确保消息正确处理** #### 1. **手动 ACK 确认** ```java channel.basicConsume(queue, false, (consumerTag, delivery) -> { // autoAck=false try { processMessage(delivery.getBody()); channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false); // 手动ACK } catch (Exception e) { // 处理失败:NACK+入队列 或 记录死信 channel.basicNack(deliveryTag, false, true); } }, consumerTag -> {}); ``` - **关键点**: - 关闭 `autoAck`(避免消息未处理完即丢失)。 - ACK 应在业务逻辑成功**后**发送。 - 失败时选择 `basicNack(requeue=true)` 或转入死信队列。 #### 2. **消费端试机制** - **本地试**:捕获异常后试(需限制最大次数)。 - **死信队列(DLX)**:超过试次数后转发到 DLX,人工干预处理。 ```java Map<String, Object> args = new HashMap<>(); args.put("x-dead-letter-exchange", "dlx.exchange"); channel.queueDeclare("order_queue", true, false, false, args); ``` --- ### **四、补充保障措施** #### 1. **网络连自动恢复** ```java ConnectionFactory factory = new ConnectionFactory(); factory.setAutomaticRecoveryEnabled(true); // 自动连 factory.setNetworkRecoveryInterval(5000); // 试间隔 ``` #### 2. **消息落库 + 状态追踪** - 生产者在发送前将消息写入本地数据库,标记状态为「发送中」。 - 通过定时任务补偿未确认的消息(类似 MQTT 的 QoS 2 机制)。 #### 3. **监控报警** - 监控指标:未确认消息数、队列堆积量、节点健康状态。 - 工具:Prometheus + Grafana、RabbitMQ Management Plugin。 --- ### **可靠性保障全路总结** | 环节 | 技术方案 | 目标 | |----------|-----------------------------------|--------------------------| | 生产者 | Confirm 模式 + 消息持久化 | 确保消息到达 Broker | | Broker | 队列持久化 + 镜像队列 | 防止消息丢失和单点故障 | | 消费者 | 手动 ACK + 死信队列 + 试 | 保证消息最终被消费 | | 兜底措施 | 本地消息表 + 监控报警 | 业务层补偿和快速故障发现 | --- ### **注意事项** 1. **性能权衡**:持久化和 Confirm 模式会增加延迟,需根据业务容忍度调整。 2. **幂等性**:试机制可能导致复消费,需结合[消息去方案](接)实现。 3. **脑裂问题**:镜像队列在集群网络分区时可能产生数据不一致,需配置 `pause_minority` 策略。 通过以上多级保障,可达到 **99.99%+** 的消息可靠性(实际取决于硬件和网络稳定性)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值