RabbitMQ工作队列模式

本文介绍了RabbitMQ的工作队列模式,解释了如何实现消息的轮询分发。在多个消费者的情况下,消息被均匀分配,消费者接收消息后先缓存,处理业务逻辑后再确认消息。手动确认消息和设置basicQos参数是实现能者多劳的关键,否则可能会出现消息处理未完成但已被确认的情况。

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

在上一章节《RabbitMQ 简单的 Hello World 程序》,我们演示了简单的队列模式。
想象一下,如果有多个消费者消费消息会怎么分配消息呢?

我们将接收消费的代码重命名为Work并复制一份。

在这里插入图片描述
同时,我们将发送消息的代码循环20次:

for (int i = 0; i < 20; i++) {
    channel.basicPublish("", Constant.QUEUE_NAME, null, ("work" + i).getBytes());
    System.out.println("message [" + "work" + i + "] be sent");
}

于是,我们看下运行结果:
在这里插入图片描述
生产了20条消息,编号分别是从019
在这里插入图片描述
Work01收到消息,是所有的偶数编号消息。
在这里插入图片描述
Work02收到消息,是所有的奇数编号消息。


autoACK=true 表示 消费者 接收到消息 就立刻回复RabbitMQ服务器 :“我收到了”。

容易混淆的点:消费者接收到消息 和 消费者处理消息业务逻辑 是 两码事。消费者可以先接收消息,把消息缓存起来,等待业务逻辑慢慢处理。

在这种模式下,RabbitMQ服务器 是轮询 分发消息的。

深入一点点:

当 消息 被生产后进入 RabbitMQ 服务器 消息队列中,每个消息是有一个编号[就像似身份证一样,唯一的不重复的] 。

每一个信道连接上 RabbitMQ 服务器 ,都会被记录下来 ,所以 RabbitMQ 服务器 知道有多少个消费者,总数用一个变量 n 表示。

RabbitMQ 服务器 将每条消息开始 推送 给消费者,如果达到一个轮询的效果,会使用一个算法,比如 使用消息的编号 如 247 248 249 ··· 等等,使用 一个变量 m 表示。

m % n ,使用 m 对 n 取余 确定该消息被发送的目标主机。这样,就可以实现一个轮询的机制。


假如 有 work01、work02 两个消费者,消息在不断的产生,假如此时work03消费者也开始消费了。那么, RabbitMQ 服务器 会将消费者增加一个,即 n = n + 1。


autoACK=false 表示 消费者 接收到消息 先放入缓存 再通过回调处理业务逻辑 需要等待业务逻辑处理完 再 回复RabbitMQ服务器 :“我收到了”。

public static void main(String[] args) throws IOException, TimeoutException {
    ConnectionFactory factory = new ConnectionFactory();
    factory.setHost("49.232.202.201");
    factory.setUsername("gosuncn");
    factory.setVirtualHost("/gosuncn");
    Connection connection = factory.newConnection();
    Channel channel = connection.createChannel();
    channel.queueDeclare(Constant.QUEUE_NAME, false, false, false, null);
    channel.basicConsume(Constant.QUEUE_NAME, false, (consumerTag, message) -> {
        System.out.println("DeliverCallback's consumerTag is {" + consumerTag + "}");
        System.out.println("消费消息的内容 = " + new String(message.getBody()));
        channel.basicAck(message.getEnvelope().getDeliveryTag(), false);
    }, consumerTag -> System.out.println("CancelCallback's consumerTag is {" + consumerTag + "}"));
}

channel.basicConsume()参数autoACK设为false,表示需要手动确认消息。

channel.basicAck(message.getEnvelope().getDeliveryTag(), false);表示 仅 确认 DeliveryTag 这一条消息。若参数二为true,则表示 确认 DeliveryTag 以及之前的所有消息。

在这里插入图片描述
目前,比如有10个消费者,那么这10个消费者收到的消息是平均的,仍然是一旦有消息被生产进入RabbitMQ服务器,服务器就用推(Push)的方式,为消费者推送消息,依然是轮询机制来分发消息,因此这10个消费者收到的消息还是平均的。

在这里插入图片描述


channel.basicQos(1);

在手动ACK的情况下,加上Qos限制,就可以使得性能优秀的服务器可以更加充分地发挥性能优势。

channel.basicQos(1);的意思是,RabbitMQ服务器发送的消息,未确认的最大数值是void basicQos(int prefetchCount)中参数prefetchCount。表示消费者预先拉拽prefetchCount条消息,如果没有确认就不会再拉取消息。所以,服务器不会按照轮询机制一窝蜂的把数据先发给消费者。

在这里插入图片描述
上图中的运行结果,感觉还是轮询分配的啊,我们将消费者的业务逻辑分别加上不同的睡眠,就看的更加清楚了。

在这里插入图片描述
很明显了吧?已经不再是轮询机制分发消息了。这时候,是真的谁的能力强谁做的就多了。能者多劳,体现的淋漓尽致。

总结一下,要想实现能者多劳公平分配,必须 ①、手动确认消息 ②、必须设置basicQos

如果不使用①、手动确认消息会出现什么呢?会出现消费还没消费完,就回复服务器确认收到的消息了,服务器就会再给该消费者分配消息。

channel.basicAck(message.getEnvelope().getDeliveryTag(), false);删除并设置autoACKtrue。会发现运行结果业务处理快的Work01把自己的任务做完了就开始休息了,而Work02Work03还在继续工作,Work02Work03要快一点,所以Work02完成任务也休息了,Work03还要继续做,直到完成任务。

为啥呢?原因很简单,消息确认 和 业务逻辑处理 分开了,并不是业务逻辑处理成功再确认消息了。所以,可以理解为消费者的一个线程负责接收消息并确认消息,把收到的消息囤起来给其他的线程去做业务逻辑处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值