一、简介
在《RabbitMQ-简单队列》中,只有一个生产者一个消费者。今天我们一起学习Work模式,即一个生产者,多个消费者,如下图所示:
需要注意的是:
(1)生产者的消息是发送到一个队列里,所以即使有两个消费者,一个消息只能被一个消费者消费。
(2)Work模式中可以分为两种模式:一两个消费者平均消费队列中的消息,即使它们的消费能力是不一样的;二能者多劳模式,消费能力强的消费者会获取更多的消息。
二、编码实现
2.1、生产者
向队列发送50条消息,生产者每生产一条消息后都会休眠一段时间。
public class Producer {
private final static String QUEUE_NAME = "work_queue";
public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
for (int i = 0; i < 50; i++) {
// 消息内容
String message = "消息:" + i;
channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
// 休眠
Thread.sleep(i * 10);
}
channel.close();
connection.close();
}
}
2.2、消费者1
public class Consumer1 {
public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QueueUtil.QUEUE_NAME_WORK, false, false, false, null);
// 定义队列的消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 监听队列,手动返回完成
channel.basicConsume(QueueUtil.QUEUE_NAME_WORK, false, consumer);
// 获取消息
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println("Consumer1 Received:" + message);
// 休眠
Thread.sleep(10);
// 返回确认状态
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
2.3、消费者2
public class Cunsumer2 {
public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QueueUtil.QUEUE_NAME_WORK, false, false, false, null);
// 定义队列的消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 监听队列,手动返回完成状态
channel.basicConsume(QueueUtil.QUEUE_NAME_WORK, false, consumer);
// 获取消息
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println("Consumer2 Received:" + message);
// 休眠1秒
Thread.sleep(1000);
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
2.4、测试
(1)启动消费者1和消费者2。
(2)启动生产者,队列work_queue中初始有50条消息待消费,随后被消费者1和消费者2消费。
(3)观察控制台。
- 消费者1和消费者2获取到的消息内容是不同的,同一个消息只能被一个消费者获取。
- 消费者1和消费者2获取到的消息的数量是相同的,一个是奇数一个是偶数。
大家看到这,可能会问,既然消费者1的能力大于消费者2(消费者1的休眠时间更短),那消费者1的消费数量是不是应该更多些呢?从一定角度上来说,应该是这样的。这也是我们接下来要描述的“能者多劳模式”,如下:
public class Consumer1 {
public static void main(String[] argv) throws Exception {
// 获取到连接以及mq通道
Connection connection = ConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QueueUtil.QUEUE_NAME_WORK, false, false, false, null);
// 同一时刻服务器只会发一条消息给消费者(上一条消息消费完了,才会消费下一条)
channel.basicQos(1);
// 定义队列的消费者
QueueingConsumer consumer = new QueueingConsumer(channel);
// 监听队列,手动返回完成
channel.basicConsume(QueueUtil.QUEUE_NAME_WORK, false, consumer);
// 获取消息
while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody());
System.out.println("Consumer1 Received:" + message);
// 休眠
Thread.sleep(10);
// 返回确认状态
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
}
}
不知道大家有没有注意到,我们在消费者1这里加了channel.basicQos(1)这行代码,用于表示同一时刻服务器只会发送一条消息给消费者。同理,我们在消费者2也加上这行代码。然后重新进行测试: