1. 程式人生 > >Spring boot 下使用RabbitMQ報錯:連結拒絕和不能建立佇列

Spring boot 下使用RabbitMQ報錯:連結拒絕和不能建立佇列

做專案時使用了rabbitMQ,本地執行沒有問題,可是部署到paas時就報錯
主要是兩個錯誤:1.連結拒絕 2.不能註冊queue 報錯如下:

**錯誤1**

Unable to connect Spring AMQP / Rabbit MQ : org.springframework.amqp.AmqpConnectException: java.net.ConnectException: Connection refused: connect
**錯誤2**

Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=404, reply-text=NOT_FOUND - no queue 'task.queue.xxx' in vhost 'ffcd3756-5bbc-4cbc-8c46-01366b37cf80', class-id=50, method-id=10)
2017-08-27T17:15:39.550+08:00 [APP/0] [OUT] at com.rabbitmq.utility.ValueOrException.getValue(ValueOrException.java:66) 2017-08-27T17:15:39.550+08:00 [APP/0] [OUT] at com.rabbitmq.utility.BlockingValueOrException.uninterruptibleGetValue(BlockingValueOrException.java:32) 2017-08-27T17:15:39.550+08:00 [APP/0] [OUT] at com.rabbitmq
.client.impl.AMQChannel$BlockingRpcContinuation.getReply(AMQChannel.java:360) 2017-08-27T17:15:39.550+08:00 [APP/0] [OUT] at com.rabbitmq.client.impl.AMQChannel.privateRpc(AMQChannel.java:225) 2017-08-27T17:15:39.550+08:00 [APP/0] [OUT] at com.rabbitmq.client.impl.AMQChannel.exnWrappingRpc(AMQChannel.java:117)

task.queue.xxx 是我的對列名稱

1. MQ配置檔案

    @Configuration
    @Profile("dev")
    static class LocalConfiguration {
        @Value("${rabbitmq.queuename}")
        private String queueName;

        @Value("${rabbitmq.exchange}")
        private String queueExchange;

        @Value("${rabbitmq.routingkey}")
        private String routingkey;

        @Value("${cf.rabbit.service.name}")
        private String rabbitService;

        @Bean
        public ConnectionFactory connectionFactory() {
            CachingConnectionFactory factory = new CachingConnectionFactory();
            factory.setUsername("guest");
            factory.setPassword("guest");
            factory.setVirtualHost("test");
            factory.setHost("localhost");
            // factory.setPort(15672);
            factory.setPublisherConfirms(true);// 保證訊息的事務性處理rabbitmq預設的處理方式為auto
                                                // ack,這意味著當你從訊息佇列取出一個訊息時,ack自動傳送,mq就會將訊息刪除。而為了保證訊息的正確處理,我們需要將訊息處理修改為手動確認的方式
            return factory;
        }

        // 配置接收端屬性,
        @Bean
        public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(
                ConnectionFactory connectionFactory) {
            SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
            factory.setConnectionFactory(connectionFactory);
            // factory.setPrefetchCount(5);//這個引數設定,接收訊息端,接收的最大訊息數量(包括使用get、consume),一旦到達這個數量,客戶端不在接收訊息。0為不限制。預設值為3.
            factory.setAcknowledgeMode(AcknowledgeMode.AUTO);// 確認模式:自動,預設
            factory.setMessageConverter(new Jackson2JsonMessageConverter());// 接收端型別轉化pojo,需要序列化
            return factory;
        }

        @Bean
        @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
        // 必須是prototype型別,不然每次回撥都是最後一個內容
        public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
            RabbitTemplate template = new RabbitTemplate(connectionFactory);
            template.setMessageConverter(new Jackson2JsonMessageConverter());// 傳送端型別轉化pojo,需要序列化
            return template;
        }

        @Bean
        public Queue queue() {
            return new Queue(queueName, true);// TRUE 持久化
        }

        // TopicExchange(*、#模糊匹配routing key,routing
        // key必須包含"."),DirectExchange,FanoutExchange(無routing key概念)
        @Bean
        public TopicExchange exchange() {
            return new TopicExchange(queueExchange);
        }

        @Bean
        public Binding binding(Queue queue, TopicExchange exchange) {
            return BindingBuilder.bind(queue).to(exchange).with(routingkey);
        }
    }

2. 修改後的MQ配置檔案

    @Configuration
    @Profile("dev")
    static class CloudConfiguration {

        @Value("${rabbitmq.queuename}")
        private String queueName;

        @Value("${rabbitmq.exchange}")
        private String queueExchange;

        @Value("${rabbitmq.routingkey}")
        private String routingkey;

        @Value("${cf.rabbit.service.name}")
        private String rabbitService;

        @Bean
        public ConnectionFactory connectionFactory() {
            CachingConnectionFactory factory = new CachingConnectionFactory();
            factory.setUsername("guest");
            factory.setPassword("guest");
            factory.setVirtualHost("test");
            factory.setHost("localhost");
            // factory.setPort(15672);
            factory.setPublisherConfirms(true);// 保證訊息的事務性處理rabbitmq預設的處理方式為auto
                                                // ack,這意味著當你從訊息佇列取出一個訊息時,ack自動傳送,mq就會將訊息刪除。而為了保證訊息的正確處理,我們需要將訊息處理修改為手動確認的方式

            Channel channel = factory.createConnection().createChannel(false);

            // 宣告queue,exchange,以及繫結
            try {
                channel.exchangeDeclare(queueExchange /* exchange名稱 */, "topic"/* 型別 */);
                // durable,exclusive,autodelete
                channel.queueDeclare(queueName, true, false, false, null); // (如果沒有就)建立Queue
                channel.queueBind(queueName, queueExchange, routingkey);
            } catch (Exception e) {
                log.error("mq declare queue exchange fail ", e);
            } finally {
                try {
                    channel.close();
                } catch (Exception e) {
                    log.error("mq channel close fail", e);
                }

            }
            return factory;
        }

        // 配置接收端屬性,
        @Bean
        public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(
                ConnectionFactory connectionFactory) {
            SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
            factory.setConnectionFactory(connectionFactory);
            // factory.setPrefetchCount(5);//這個引數設定,接收訊息端,接收的最大訊息數量(包括使用get、consume),一旦到達這個數量,客戶端不在接收訊息。0為不限制。預設值為3.
            factory.setAcknowledgeMode(AcknowledgeMode.AUTO);// 確認模式:自動,預設
            factory.setMessageConverter(new Jackson2JsonMessageConverter());// 接收端型別轉化pojo,需要序列化
            return factory;
        }

        @Bean
        @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
        // 必須是prototype型別,不然每次回撥都是最後一個內容
        public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {

            RabbitTemplate template = new RabbitTemplate(connectionFactory);
            template.setMessageConverter(new Jackson2JsonMessageConverter());// 傳送端型別轉化pojo,需要序列化
            return template;
        }

測試發現是因為queue和exchange建立不成功導致的連線失敗
剛開始我的解決辦法是手動在rabbitMQ客戶端建立queue和exchange,之後手動跟routingkey繫結,但是治標不治本,不是一個根本的解決辦法,最後改了下MQ的配置檔案,上2。再次執行就沒有問題了。

總結:原因可能是rabbitMQ版本的問題,paas容器用的rabbitMQ版本是3.6.6,而我本地開發用的是3.6.10,不同的版本寫法有可能不同
我的猜測哈~,知道根本原因的可以私密我^^