(内容均来自RabbitMQ官网:https://www.rabbitmq.com/tutorials/tutorial-two-java.html)
上一篇学习了RabbitMQ的"HelloWorld",导入AMQP的客户端依赖,然后封装一个获取RabbitMQ连接的简单工厂工具类,这一篇我们就学习下官方的WorkQueue(工作队列)。和HelloWorld不同之处就是WorkQueue从 一个消费者变成了多个。
图例
生产者
我们申明了一个为hello的队列,然后用for循环往hello队列中发送了20条内容为:{i}Hello RabbitMQ的信息
import com.booyue.tlh.utils.RabbitMQUtils;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import java.io.IOException;
public class Producer {
public static final String QUEUE_NAME = "hello";
private static final String MESSAGE = " Hello RabbitMQ";
public static void main(String[] args) throws IOException {
//获取一个连接
Connection connection = RabbitMQUtils.getConnection();
//获取一个通道
Channel channel = connection.createChannel();
//声明一个队列,名字为“hello”
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
//往“hello”队列里发送20条"{i} Hello RabbitMQ"的信息
for (int i = 0; i < 20; i++) {
channel.basicPublish("", QUEUE_NAME, null, (i+MESSAGE).getBytes());
}
}
}
消费者01和消费者02
消费者01和02不同点就是在模拟它们各自的实际工作的耗时上不一样,消费者01的工作耗时为1秒,消费者02的工作耗时为2秒(这里只列出了Consumer01,Consumer02不同之处就是Thread.sleep(2000))
import com.booyue.tlh.utils.RabbitMQUtils;
import com.rabbitmq.client.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
@Slf4j
public class Consumer01 {
public static void main(String[] args) throws IOException {
//获取连接
Connection connection = RabbitMQUtils.getConnection();
//获取通道
Channel channel = connection.createChannel();
//这里也可以声明一个和生产者里面声明的一样的队列,不然如果先启动消费这的话系统会报错,说系统没有对应的queue。多出声明也没关系,第一次声明之后系统就不会在创建对应的queue了
channel.queueDeclare(Producer.QUEUE_NAME, false, false, false, null);
/**
* 接收消息
* 参数1:队列名
* 参数2:是否自动确认
* 参数3:消息回调接口
*/
channel.basicConsume(Producer.QUEUE_NAME, true, new DefaultConsumer(channel) {
@SneakyThrows
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) {
String message = new String(body, "utf-8");
try {
//模拟完成业务逻辑的耗时
Thread.sleep(1000);
} finally {
log.info("消费者1收到的信息:{}", message);
}
}
});
}
}
启动生产者和两个消费者,查看两个消费者收到的消息
10:41:00.862 [pool-1-thread-4] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:1 Hello RabbitMQ
10:41:01.866 [pool-1-thread-4] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:3 Hello RabbitMQ
10:41:02.867 [pool-1-thread-4] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:5 Hello RabbitMQ
10:41:03.868 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:7 Hello RabbitMQ
10:41:04.869 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:9 Hello RabbitMQ
10:41:05.869 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:11 Hello RabbitMQ
10:41:06.869 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:13 Hello RabbitMQ
10:41:07.870 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:15 Hello RabbitMQ
10:41:08.870 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:17 Hello RabbitMQ
10:41:09.871 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:19 Hello RabbitMQ
10:41:00.861 [pool-1-thread-4] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:0 Hello RabbitMQ
10:41:01.865 [pool-1-thread-4] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:2 Hello RabbitMQ
10:41:02.866 [pool-1-thread-4] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:4 Hello RabbitMQ
10:41:03.867 [pool-1-thread-4] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:6 Hello RabbitMQ
10:41:04.868 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:8 Hello RabbitMQ
10:41:05.868 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:10 Hello RabbitMQ
10:41:06.868 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:12 Hello RabbitMQ
10:41:07.869 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:14 Hello RabbitMQ
10:41:08.869 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:16 Hello RabbitMQ
10:41:09.869 [pool-1-thread-5] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:18 Hello RabbitMQ
我们看到尽管两个消费者的工作耗时即使不一样,它们两个收到的消息数量都是一样的。原因是在自动确认的情况下,系统会默认按照顺序将每一条消息发送给下一个消费者,平均而言每个消费者会获得相同数量的消息,这种方式称为:轮询。也就是说,不管你的众多消费者中其中某一个或者多个的压力有多大,它获得的消息和其他消费者都是一样的(除非消费者关闭通道或者断开连接)。但是现实中,我们多个消费者可能分布在多态机器上,有的机器性能好有的性能差,很明显这种方式不是很合理。
公平调度
我们可以采用公平调度的方式,根据消费者处理消息的能力来接受消息的数量。
图例
生产者
和上面的生产者一样用for循环往hello队列中发送了20条内容为:{i}Hello RabbitMQ的信息
消费者01和消费者02
和上面不同的地方就是在接受消息之前告诉系统一次发送一条信息( channel.basicQos(1))和不采用自动确认的方式(autoAck=false),等完成处理操作之后告诉服务器(channel.basicAck(envelope.getDeliveryTag(), false))
import com.booyue.tlh.utils.RabbitMQUtils;
import com.rabbitmq.client.*;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
@Slf4j
public class Consumer01 {
public static void main(String[] args) throws IOException {
//获取连接
Connection connection = RabbitMQUtils.getConnection();
//获取通道
Channel channel = connection.createChannel();
//这里也可以声明一个和生产者里面声明的一样的队列,不然如果先启动消费这的话系统会报错,说系统没有对应的queue。多出声明也没关系,第一次声明之后系统就不会在创建对应的queue了
channel.queueDeclare(Producer.QUEUE_NAME, false, false, false, null);
//每次给消费者发送一条系信息,直到他告诉RabbitMQ服务器已经完成该条消息,才会收到下一条消息
channel.basicQos(1);
/**
* 接收消息
* 参数1:队列名
* 参数2:是否自动确认
* 参数3:消息回调接口
*/
channel.basicConsume(Producer.QUEUE_NAME, false, new DefaultConsumer(channel) {
@SneakyThrows
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) {
String message = new String(body, "utf-8");
try {
//模拟完成业务逻辑的耗时
Thread.sleep(1000);
} finally {
log.info("消费者1收到的信息:{}", message);
//拿到标识,确认己经收到该条信息
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
});
}
}
启动生产者和两个消费者,查看两个消费者收到的消息
很明显,由于消费者01的工作能力强(耗时只有1秒),它处理消息的数量也就多
11:23:41.806 [pool-1-thread-16] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:0 Hello RabbitMQ
11:23:42.806 [pool-1-thread-17] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:2 Hello RabbitMQ
11:23:43.806 [pool-1-thread-18] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:3 Hello RabbitMQ
11:23:44.807 [pool-1-thread-19] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:5 Hello RabbitMQ
11:23:45.808 [pool-1-thread-20] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:7 Hello RabbitMQ
11:23:46.808 [pool-1-thread-21] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:8 Hello RabbitMQ
11:23:47.808 [pool-1-thread-22] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:10 Hello RabbitMQ
11:23:48.810 [pool-1-thread-23] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:11 Hello RabbitMQ
11:23:49.810 [pool-1-thread-24] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:13 Hello RabbitMQ
11:23:50.811 [pool-1-thread-25] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:14 Hello RabbitMQ
11:23:51.812 [pool-1-thread-26] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:16 Hello RabbitMQ
11:23:52.812 [pool-1-thread-3] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:17 Hello RabbitMQ
11:23:53.813 [pool-1-thread-4] INFO com.booyue.tlh.hellowrold.Consumer01 - 消费者1收到的信息:19 Hello RabbitMQ
11:23:42.806 [pool-1-thread-18] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:1 Hello RabbitMQ
11:23:44.807 [pool-1-thread-19] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:4 Hello RabbitMQ
11:23:46.808 [pool-1-thread-20] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:6 Hello RabbitMQ
11:23:48.809 [pool-1-thread-21] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:9 Hello RabbitMQ
11:23:50.809 [pool-1-thread-22] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:12 Hello RabbitMQ
11:23:52.809 [pool-1-thread-23] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:15 Hello RabbitMQ
11:23:54.810 [pool-1-thread-24] INFO com.booyue.tlh.hellowrold.Consumer02 - 消费者2收到的信息:18 Hello RabbitMQ
其他
还是上一篇说到的,例子中没有看到交换机(Exchange)的影子,但是我们心里一定要清楚,系统一定是有使用交换机的。为我们后面更好的理解RabbitMQ的整个流程做一定的铺垫。