RabbitMQ通信方式之Work queues

RabbitMQ的WorkQueues模式用于平衡多个消费者的负载,消息被循环分发给消费者。生产者发送消息到队列,每个消费者通过手动ack确认消息处理,结合流控控制消费速度。消费者处理能力不同,如消费者1号处理速度快,消费者2号处理速度慢。自动ack和手动ack提供不同的消息确认策略,手动ack确保消息不丢失但需处理异常情况。

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

RabbitMQ通信方式之Work queues

Work Queues通信方式结构图

Work Queues与Hello World异同点:

  • Hello World是一个生产者和一个消费者。

  • Work Queues是一个生产者和多个消费者。

  • Work Queues和Hello World的形式是一样的,都是将消息推送到默认交换机。

RabbitMQ会将消息以循环的方式发送给多个消费者,以平均负载。

  1. 添加RabbitMQ和Junit工具栏的maven依赖

<!--rabbitmq-->
<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
<version>5.12.0</version>
</dependency>
<!--junit-->
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>
  1. 创建一个构建连接的工具类

public class RabbitMQConnectionUtil {
    public static final String RABBITMQ_HOST = "xxx.xx.xxx.xxx";   //设置你自己的RabbitMQ服务ip
    public static final int RABBITMQ_PORT = 5672;
    public static final String RABBITMQ_USERNAME = "guest";
    public static final String RABBITMQ_PASSWORD = "guest";
    public static final String RABBITMQ_VIRTUAL_HOST = "/";
    /**
     * 构建RabbitMQ的连接对象
     */
        public static Connection getConnection() throws Exception {
        //1. 创建Connection工厂
        ConnectionFactory factory = new ConnectionFactory();

        //2. 设置RabbitMQ的连接信息
        factory.setHost(RABBITMQ_HOST);
        factory.setPort(RABBITMQ_PORT);
        factory.setUsername(RABBITMQ_USERNAME);
        factory.setPassword(RABBITMQ_PASSWORD);
        factory.setVirtualHost(RABBITMQ_VIRTUAL_HOST);

        //3. 返回连接对象
        Connection connection = factory.newConnection();
        return connection;
    }
  1. 创建生产者

public class Producer {
    public final static String QUEUE_NAME = "hello";
    @Test
    public void publish() throws Exception {
        // 创建连接工厂
        Connection connection = RabbitMQConnectionUtil.getConnection();

        // 创建通道
        Channel channel = connection.createChannel();
        // 声明队列
        channel.queueDeclare(QUEUE_NAME, false, false, false, null);
        // 循环发送10次消息
        for (int i = 0; i < 10; i++) {
            String message = "Hello World!"+i;
            channel.basicPublish("", QUEUE_NAME, null, message.getBytes(StandardCharsets.UTF_8));
        }
        System.out.println("消息发送成功!");
    }
}		
  1. 创建多个消费者

public class Consumer {
	
    /**
     * 消费者1号
     */
    @Test
    public void consume1() throws Exception {
        //1. 获取连接对象
        Connection connection = RabbitMQConnectionUtil.getConnection();

        //2. 构建Channel
        Channel channel = connection.createChannel();

        //3. 构建队列
        channel.queueDeclare(Producer.QUEUE_NAME,false,false,false,null);
		//3.5 设置消息的流控
        channel.basicQos(1);

        //4. 监听消息
        DefaultConsumer callback = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                try {
                    //睡0.1s来模拟该消费者消费能力较强
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("消费者1号获取到消息:" + new String(body,"UTF-8"));
                //手动ack(确认消息已经被处理)
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        //将第二个参数设为false意为关闭自动ack
        channel.basicConsume(Producer.QUEUE_NAME,false,callback);
        System.out.println("1号开始监听队列");

        System.in.read();
    }

    /**
     * 消费者2号
     */
    @Test
    public void consume2() throws Exception {
        //1. 获取连接对象
        Connection connection = RabbitMQConnectionUtil.getConnection();

        //2. 构建Channel
        Channel channel = connection.createChannel();
	
        //3. 构建队列
        channel.queueDeclare(Producer.QUEUE_NAME,false,false,false,null);
        
		//3.5 设置消息的流控
        channel.basicQos(1);

        //4. 监听消息
        DefaultConsumer callback = new DefaultConsumer(channel){
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                try {
                    //睡0.5s来模拟该消费者消费能力较弱
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("消费者2号获取到消息:" + new String(body,"UTF-8"));
                //手动ack(确认消息已经被处理)
                channel.basicAck(envelope.getDeliveryTag(),false);
            }
        };
        //将第二个参数设为false意为关闭自动ack
        channel.basicConsume(Producer.QUEUE_NAME,false,callback);
        System.out.println("2号开始监听队列");

        System.in.read();
    }
}
  1. 开启两个消费者以及生产者

  1. 观察消费1号状态

  1. 观察消费2号状态

上述例子可以看到,多个消费者监听同一个队列时,我们可以通过手动ack加流控的方式控制它们的消费能力。

自动ack和手动ack是RabbitMQ中确认消息的两种方式:

自动ack:当消费者从队列中取出消息并开始处理时,RabbitMQ会立即确认该消息,即将其从队列中删除,无需等待消费者处理完成。这种方式适合于消息处理时间很短、消息丢失不会产生严重后果的场景,可以提高消息处理的吞吐量。

手动ack:消费者在处理完一条消息后,需要向RabbitMQ发送一个确认消息的指令,以告诉RabbitMQ该消息已经被成功处理。如果消费者在处理消息时出现了异常,那么就不会发送确认消息,这样RabbitMQ会重新将该消息发送给其他消费者进行处理。手动ack的好处是可以确保消息不会丢失,同时也可以控制消息的处理顺序和速度。但是,如果消费者在处理消息时出现了异常,就需要考虑如何处理未确认的消息,避免消息丢失或者重复消费的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

像鸟一样菜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值