工作队列(又称任务队列)的主要思想是为了避免立即执行资源密集型任务,而不得不等待他完成。其作用就是将需要处理的任务放在队列(先进先出)中,一个工作进程可以取出任务并完成工作。如果启动了多个工作进程,那么这些工作线程会一起处理这些任务。
好比工厂的流水线,如果流水线上有300个产品,而工位上只有一位工人在给产品加工,可想而知其效率低下。如果在流水线后多安排几个工位,那么就可以提高产品加工速度(注意:一个产品只能被加工一次,不能被多次处理,要防止重复“加工”)。
轮询发消息
概念
rabbitmq队列处理机制默认是轮询的,即轮流按工作顺序将任务分配给工作线程(循环分配任务)。
实例代码
生产者首先往队列中加入15条消息,成功加入队列后等待消费者消费。生产者代码如下:
public static final String QUEUE_NAME = "rabbit_queue";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = RabbitConnectionUtil.getConnection();
// 获取信道
Channel channel = connection.createChannel();
for (int i = 0; i < 15; i++) {
/**
* 生成队列
* queue 队列名称
* durable 是否持久化
* exclusive 是否独占队列
* autoDelete 是否自动删除队列(服务器不再使用时自动删除队列)
* arguments 队列参数
*/
HashMap<String, Object> param = new HashMap<>();
param.put("name", "梦尘");
param.put("action", "添加了一个消息");
channel.queueDeclare(QUEUE_NAME, false, false, false, param);
/**
* exchange 交换机
* 路由的key值
* 其他参数信息
* 发送消息的消息体
*/
String message = "给您发了一条消息";
channel.basicPublish("", QUEUE_NAME, null, message.getBytes(StandardCharsets.UTF_8));
System.out.println("消息发送完毕!");
}
}
创建5个消费者,监听到队列中有任务时进行工作。消费者代码如下:
public class Work01 implements Runnable {
public static final String QUEUE_NAME = "rabbit_queue";
public static void main(String[] args) {
Runnable work01 = new Work01();
new Thread(work01, "A").start();
new Thread(work01, "B").start();
new Thread(work01, "C").start();
new Thread(work01, "D").start();
new Thread(work01, "E").start();
}
@Override
public void run() {
try {
String threadName = Thread.currentThread().getName();
Connection connection = RabbitConnectionUtil.getConnection();
Channel channel = connection.createChannel();
// 消息接收
DeliverCallback deliverCallback = (consumerTag, message) -> {
System.out.println("Work01 => " + threadName + "接收到消息:" + new String(message.getBody()));
};
// 消息取消监听
CancelCallback cancelCallback = (consumerTag) -> {
System.out.println(consumerTag + "消息者取消消息发送!");
};
// 消息发送
try {
channel.basicConsume(QUEUE_NAME, true, deliverCallback, cancelCallback);
} catch (IOException e) {
throw new RuntimeException(e);
}
} catch (IOException | TimeoutException e) {
throw new RuntimeException(e);
}
}
}
运行结果:
可以看到里面成功添加了15条消息,接下来运行消费者查询观察消费过程。
消费者运行结果:
总结
工作队列是学习RabbitMQ的一个非常重要知识点,通过利用队列的先进先出的特点,将工作顺序分配给工作线程执行。
思考
如果D任务在被消费者处理过程中因某些原因出现bug导致任务中断,而这时候队列已经将任务分配出去并清除了队列里的任务D,这就意味着我们丢失了这条未处理完毕的任务,那么我们该怎么防止任务丢失的情况?