rabbitmq 操作记录

当前记录的版本为 springboot2 及以上版本

问题一 :同一个队列,两个不同类型的消费者监听(一个消费者为手动ack,一个消费者为自动ack) ,当消息数量不多的时候,消费日志打印信息只有其中某一个队列在处理。没有办法两个队列同时工作。

rabbitmq 监听消费消息 java rabbitmq监听多个队列_spring

当前只有默认的5个消费者

测试 消费端方法进入直接 手动ACK 之后,然后休眠等待10min查看队列数据信息

消费端代码如下,当修改端启动之后,

package com.example.customer;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

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

@SpringBootApplication
public class CustomerApplication {

    public static void main(String[] args) {

        SpringApplication.run(CustomerApplication.class, args);
    }

    @Component
    public class Customer {


        @Bean
        public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(@Autowired ConnectionFactory
                                                                                                 connectionFactory) {

            SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
            // 设置消费者个数
            factory.setConcurrentConsumers(5);
            factory.setMaxConcurrentConsumers(10);
            factory.setConnectionFactory(connectionFactory);
            // 设置此容器需要 手动ack
            factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
            return factory;
        }


        @RabbitListener(queues = "local_test",
                containerFactory = "simpleRabbitListenerContainerFactory")
        public void test(String message1, Message message, Channel channel) {

            try {
                //   每个消费者第一次进入此代码之后, 消息队列中的会直接丢弃
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                System.out.println("----------->" + message1);
                // 此时会休眠等待 
                TimeUnit.MINUTES.sleep(10L);
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

}

执行后。消费端数据信息

rabbitmq 监听消费消息 java rabbitmq监听多个队列_System_02


控制台信息

rabbitmq 监听消费消息 java rabbitmq监听多个队列_java_03

新增 一个自动ACK的消费端
package com.example.customer;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

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

@SpringBootApplication
public class CustomerApplication {

    public static void main(String[] args) {

        SpringApplication.run(CustomerApplication.class, args);
    }

    @Component
    public class Customer {


        @Bean
        public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(@Autowired ConnectionFactory
                                                                                                 connectionFactory) {

            SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
            factory.setConcurrentConsumers(5);
            factory.setMaxConcurrentConsumers(10);
            factory.setConnectionFactory(connectionFactory);
            // 设置此容器需要 手动ack
            factory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
            return factory;
        }


        @RabbitListener(queues = "local_test",
                containerFactory = "simpleRabbitListenerContainerFactory")
        public void test(String message1, Message message, Channel channel) {

            try {
                //   每个消费者第一次进入此代码之后, 消息队列中的会直接丢弃
                channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
                System.out.println("----------->" + message1);
                // 此时会休眠等待,此后不会
                TimeUnit.MINUTES.sleep(10L);
            } catch (IOException | InterruptedException e) {
                e.printStackTrace();
            }
        }

		//新增 自动ack 
        @RabbitListener(queues = "local_test")
        public void test1(String message1, Message message, Channel channel) {

            System.out.println("消费者2 -->" + message1);
        }

    }

}

当前消息数量

rabbitmq 监听消费消息 java rabbitmq监听多个队列_spring_04

项目启动,发现消费者数量增加,但是控制台并没有打印消费者2的消费日志信息;打印的依旧是 5个消费者1的日志信息

rabbitmq 监听消费消息 java rabbitmq监听多个队列_spring_05


再看mq管理页面信息; 5个消息被消费了;还有1230个等待ACK

rabbitmq 监听消费消息 java rabbitmq监听多个队列_java_06

正常理解应该是 消费2会把后面消息消费掉才对,但是实际并没有消费; 原因是因为
当前每个消费者的 prefetch count 预取消息数量为 250 ;

rabbitmq 监听消费消息 java rabbitmq监听多个队列_System_07

由上面的代码 ,有5个队列监听参数使用了 SimpleRabbitListenerContainerFactory 来执行手动ACK; 每个消费者预取消息数为250; 5个可预取的消息数为 5 * 250 = 1250 条;导致 队列的1235个消息(1235 < 1250)被前5个队列 预取,导致消费者2没有可消费的消息。所以消费者2 控制台没有打印任何日志信息;

修改待消费数量,验证消费者2会把 剩余的消息消费掉

新发送一万条消息

rabbitmq 监听消费消息 java rabbitmq监听多个队列_spring_08

开启消费端。消费结果

控制台打印日志信息

rabbitmq 监听消费消息 java rabbitmq监听多个队列_java_09

mq管理页面消息数量

rabbitmq 监听消费消息 java rabbitmq监听多个队列_java_10


有1250个消息待ACK

问题答案

当前的消息预处理数较多,消息数量不多,导致所有消息被其中的一个队列全部拉取到了,另外一个队列无法获取消费