RabbitMQ入门(2)——工作队列
前面介绍了队列接收和发送消息,这篇将学习如何创建一个工作队列来处理在多个消费者之间分配耗时的任务。工作队列(work queue),又称任务队列(task queue)。
工作队列的目的是为了避免立刻执行资源密集型任务、减少等待时间。将消息发送到队列,工作进程在后台从队列取出任务并处理。
准备
通过Thread.sleep()来模拟耗时的任务,通过在消息的末尾添加"."来表示处理时间,例如,Hello...表示耗时3秒。
发送端
NewTask.java:
package com.xxyh.rabbitmq;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class NewTask {
private static final String WORK_NAME = "task_queue";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
boolean durable = true;
channel.queueDeclare(WORK_NAME, durable, false, false, null);
// 发送10条记录,每次在后面添加"."
for (int i = 0; i < 10; i++) {
String dot = "";
for (int j = 0; j <= i; j++) {
dot += ".";
}
String message = "work queue " + dot + dot.length();
channel.basicPublish("", WORK_NAME,
MessageProperties.PERSISTENT_TEXT_PLAIN,
message.getBytes("utf-8"));
System.out.println(Thread.currentThread().getName() + "发送消息:" + message);
}
channel.close();
connection.close();
}
}
接收端
Work.java:
package com.xxyh.rabbitmq;
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.concurrent.TimeoutException;
public class Work {
private static final String WORK_QUEUE = "task_queue";
public static void main(String[] args) throws IOException, TimeoutException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
boolean durable = true;
channel.queueDeclare(WORK_QUEUE, durable, false, false, null);
// 设置同一个消费者在同一时间只能消费一条消息
channel.basicQos(1);
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println(Thread.currentThread().getName() + "接收消息:" + message);
try {
doWork(message);
System.out.println("消息接收完毕");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 确认消息已经收到
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
// 取消autoAck
boolean autoAck = false;
channel.basicConsume(WORK_QUEUE, autoAck, consumer);
}
private static void doWork(String task) throws InterruptedException {
for (char ch : task.toCharArray()) {
if (ch == '.') {
Thread.sleep(1000);
}
}
}
}
Round-robin分发
使用任务队列的好处就是容易处理并发工作。如果积累了大量的工作,只需要增加工作者就可以了。对于上面的示例代码,可以启动两个Work,然后启动NewTask,执行结果如下:
Work1:
pool-1-thread-4接收消息:work queue .1
消息接收完毕
pool-1-thread-5接收消息:work queue ...3
消息接收完毕
pool-1-thread-6接收消息:work queue .....5
消息接收完毕
pool-1-thread-7接收消息:work queue .......7
消息接收完毕
pool-1-thread-8接收消息:work queue .........9
消息接收完毕
Work2:
pool-1-thread-4接收消息:work queue ..2
消息接收完毕
pool-1-thread-5接收消息:work queue ....4
消息接收完毕
pool-1-thread-6接收消息:work queue ......6
消息接收完毕
pool-1-thread-7接收消息:work queue ........8
消息接收完毕
pool-1-thread-8接收消息:work queue ..........10
消息接收完毕
NewTask:
main发送消息:work queue .1
main发送消息:work queue ..2
main发送消息:work queue ...3
main发送消息:work queue ....4
main发送消息:work queue .....5
main发送消息:work queue ......6
main发送消息:work queue .......7
main发送消息:work queue ........8
main发送消息:work queue .........9
main发送消息:work queue ..........10
默认情况下,RabbitMQ会依次发送消息到下一个队列。一般情况下,每个消费者会收到数量大致相同的消息。这种分发消息的方法称为round-robin。
消息确认(Message acknowledgment)
处理一个任务可能需要几秒钟的时间。如果一个消费者在执行过程中中断可能导致消息的丢失,显然这并不是我们希望的,RabbitMQ提供了消息确认机制,让消费者反馈已经收到消息的信息,然后RabbitMQ就可以自由产出这条消息了。
如果消费者中断了,没有发送确认消息,RabbitMQ会认为消息发送失败并将消息重新加入队列。如果同时有其他消费者在工作,它会马上重新发送该消息到另一个消费者。这种方式可以保证消息不丢失。
消息确认机制默认是开启的:
// 打开确认
boolean autoAck = false;
channel.basicConsume(queueName, autoAck, consumer);
消息持久化(Message durability)
上面的方式可以保证某个消费者中断时,消息不会丢失。然而,如果RabbitmqMQ中断或崩溃,它是不会记住队列和消息的。为了实现消息不丢失,需要将队列和消息持久化。首先将队列持久化:
boolean durable = true;
channel.queueDeclare("hello", durable, false, false, null);
如果一个已经存在的队列没有被设置持久化(durable=false)的,是不能更改它的参数的。另外,必须在消息的发送端、可接收端同时修改队列的声明。
上面的设置保证了即使RabbitMQ重启,队列也不丢失。但是并不能保证消息一定能发送到接收端,还需要将消息持久化:
channel.basicPublish("", QUEUE_NAME,
MessageProperties.PERSISTENT_TEXT_PLAIN,
message.getBytes("utf-8"));
公平分发
在某些情况下,分发的效果并不让人满意。例如,在两个消费者的情况下,奇数任务多而偶数任务少,可能一个消费队列一直处于繁忙状态,而另一个几乎处于闲置状态。RabbitMQ并不知晓这一情况,它只负责分发队列里的消息,不考虑消息未确认的情况。它只是盲目地将第n个消息发送给第n个消费者。
channel.basicQos(1);
这行代码的作用是告诉RabbitMQ,不要在同一时间给同一个消费者超过1条消息。
RabbitMQ入门(2)——工作队列的更多相关文章
- RabbitMQ入门:工作队列(Work Queue)
在上一篇博客<RabbitMQ入门:Hello RabbitMQ 代码实例>中,我们通过指定的队列发送和接收消息,代码还算是比较简单的. 假设有这一些比较耗时的任务,按照上一次的那种方式, ...
- RabbitMQ入门教程——工作队列
什么是工作队列 工作队列是为了避免等待一些占用大量资源或时间操作的一种处理方式.我们把任务封装为消息发送到队列中,消费者在后台不停的取出任务并且执行.当运行了多个消费者工作进程时,队列中的任务将会在每 ...
- RabbitMQ入门:总结
随着上一篇博文的发布,RabbitMQ的基础内容我也学习完了,RabbitMQ入门系列的博客跟着收官了,以后有机会的话再写一些在实战中的应用分享,多谢大家一直以来的支持和认可. RabbitMQ入门系 ...
- RabbitMQ入门:发布/订阅(Publish/Subscribe)
在前面的两篇博客中 RabbitMQ入门:Hello RabbitMQ 代码实例 RabbitMQ入门:工作队列(Work Queue) 遇到的实例都是一个消息只发送给一个消费者(工作者),他们的消息 ...
- RabbitMQ入门(6)——远程过程调用(RPC)
在RabbitMQ入门(2)--工作队列中,我们学习了如何使用工作队列处理在多个工作者之间分配耗时任务.如果我们需要运行远程主机上的某个方法并等待结果怎么办呢?这种模式就是常说的远程过程调用(Remo ...
- RabbitMQ入门(3)——发布/订阅(Publish/Subscribe)
在上一篇RabbitMQ入门(2)--工作队列中,有一个默认的前提:每个任务都只发送到一个工作人员.这一篇将介绍发送一个消息到多个消费者.这种模式称为发布/订阅(Publish/Subscribe). ...
- RabbitMQ入门到进阶(Spring整合RabbitMQ&SpringBoot整合RabbitMQ)
1.MQ简介 MQ 全称为 Message Queue,是在消息的传输过程中保存消息的容器.多用于分布式系统 之间进行通信. 2.为什么要用 MQ 1.流量消峰 没使用MQ 使用了MQ 2.应用解耦 ...
- RabbitMQ入门教程(四):工作队列(Work Queues)
原文:RabbitMQ入门教程(四):工作队列(Work Queues) 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https:/ ...
- RabbitMQ入门(二)工作队列
在文章RabbitMQ入门(一)之Hello World,我们编写程序通过指定的队列来发送和接受消息.在本文中,我们将会创建工作队列(Work Queue),通过多个workers来分配耗时任务. ...
随机推荐
- K线数据库表结构
-- -- 数据库: `bittrex` -- -- -------------------------------------------------------- -- -- 表的结构 `ltc` ...
- 3.2 - FTP文件上传下载
题目:开发一个支持多用户同时在线的FTP程序要求:1.用户加密认证2.允许同时多用户登录3.每个用户有自己的家目录,且只能访问自己的家目录4.对用户进行磁盘配额,每个用户的可用空间不同5.允许用户在f ...
- 安全测试之bWAPP环境搭建
本篇文章介绍独立安装部署bwapp. 安装环境:window7+IIS7+mysql5.6+php5.6 bWAPP下载地址:https://sourceforge.net/projects/bwap ...
- 对Numpy数组按axis运算的理解
Python的Numpy数组运算中,有时会出现按axis进行运算的情况,如 >>> x = np.array([[1, 1], [2, 2]]) >>> x arr ...
- SpringBoot安装和创建简单的Web应用
SpringBoot安装 方式一: Eclipese->Help->Eclipse Marketplace ->Finde STS -> Install 注意:安装过程中挺慢, ...
- tomcat查看GC信息
tomcat启动参数,将JVM GC信息写入tomcat_gc.log CATALINA_OPTS='-Xms512m -Xmx4096m -XX:PermSize=64M -XX:MaxNewSiz ...
- 『HTML5挑战经典』是英雄就下100层-开源讲座(一)从天而降的英雄
是英雄就下100层是一款经典的手机小游戏,以前是在诺基亚手机上十分有名.今天我们就用HTML5和lufylegend一步步地实现它. 一,准备工作 首先,你需要下载lufylegend,下载地址如下: ...
- centos7 最小化安装后的配置优化
echo #CENTOS7echo #1.最小化安装之后需要做的事echo 2.配置echo 2.1 安装网络yum install net-tools -y echo 2.2 更新机器名echo h ...
- 利用arcgis制作出 源解析要用的ASCII文件
准备:1.确定好模拟区域范围,精度,行列数 2.确定好源解析的城市规划 思路: 1.全国省级图+本地市县图-->合成一张区域图(联合) 合成之后,添加一个字段,一个数字类型字段 ...
- RedisTemplate访问Redis数据结构
https://www.jianshu.com/p/7bf5dc61ca06 Redis 数据结构简介 Redis 可以存储键与5种不同数据结构类型之间的映射,这5种数据结构类型分别为String(字 ...