work queues 工作队列

1、模型图:

为什么会出现 work queues?
前提:使用 simple 队列的时候
我们应用程序在是使用消息系统的时候,一般生产者 P 生产消息是毫不费力的(发送消息即可),
而消费者接收完消息后的需要处理,会耗费一定的时间,这时候,就有可能导致很多消息堆积在
队列里面,一个消费者有可能不够用
 
那么怎么让消费者同事处理多个消息呢?
在同一个队列上创建多个消费者,让他们相互竞争,这样消费者就可以同时处理多条消息了
 
使用任务队列的优点之一就是可以轻易的并行工作。如果我们积压了好多工作,我们可以通
过增加工作者(消费者)来解决这一问题,使得系统的伸缩性更加容易
 

2、代码实例(轮询分发)

生产者进行生产消息

import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.util.ConnectionUtils;
public class Send { private static final String QUEUE_NAME ="test_work_queue"; public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//获取连接
Connection conn = ConnectionUtils.getConnection(); //获取Channel
Channel channel= conn.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false,null); //连续发送50个消息
for(int i = ; i<=;i++){
String msg = "work" + i;
channel.basicPublish("",QUEUE_NAME, null, msg.getBytes());
Thread.sleep();
}
channel.close();
conn.close();
}
}

消费者1:

import java.io.IOException;
import java.util.concurrent.TimeoutException;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.util.ConnectionUtils;
public class WorkReceive { private static final String QUEUE_NAME ="test_work_queue"; public static void main(String[] args) throws IOException, TimeoutException { Connection conn = ConnectionUtils.getConnection(); Channel channel = conn.createChannel();
//声明队列
channel.queueDeclareNoWait(QUEUE_NAME, false, false, false, null);
//定义一个消费者
Consumer consumer = new DefaultConsumer(channel){
//收到消息就会触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String msg = new String(body,"utf-8");
System.out.println("消费者1接收到的消息" + msg); try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
System.out.println("消费者1处理完成!");
}
}
};
//监听队列
boolean autoAck = true;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}

消费者2:

import java.io.IOException;
import java.util.concurrent.TimeoutException; import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.util.ConnectionUtils; public class WorkReceive2 { private static final String QUEUE_NAME ="test_work_queue"; public static void main(String[] args) throws IOException, TimeoutException { Connection conn = ConnectionUtils.getConnection(); Channel channel = conn.createChannel();
//声明队列
channel.queueDeclareNoWait(QUEUE_NAME, false, false, false, null);
//定义一个消费者
Consumer consumer = new DefaultConsumer(channel){
//收到消息就会触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String msg = new String(body,"utf-8");
System.out.println("消费者2接收到的消息" + msg); try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
System.out.println("消费者2处理完成!");
}
}
};
//监听队列
boolean autoAck = true;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}

提前开启消费者进行对消息队列的监听!!!

此时开启消息的生产:
消费者2:
消费者2接收到的消息work2
消费者2处理完成!
消费者2接收到的消息work4
消费者2处理完成!
消费者2接收到的消息work6
消费者2处理完成!
消费者2接收到的消息work8
消费者2处理完成!
消费者2接收到的消息work10
消费者2处理完成!
消费者2接收到的消息work12
消费者2处理完成!
消费者2接收到的消息work14
消费者2处理完成!
消费者2接收到的消息work16
消费者2处理完成!
消费者2接收到的消息work18
消费者2处理完成!
消费者2接收到的消息work20
消费者2处理完成!
消费者2接收到的消息work22
消费者2处理完成!
消费者2接收到的消息work24
消费者2处理完成!
消费者2接收到的消息work26
消费者2处理完成!
消费者2接收到的消息work28
消费者2处理完成!
消费者2接收到的消息work30
消费者2处理完成!
消费者2接收到的消息work32
消费者2处理完成!
消费者2接收到的消息work34
消费者2处理完成!
消费者2接收到的消息work36
消费者2处理完成!
消费者2接收到的消息work38
消费者2处理完成!
消费者2接收到的消息work40
消费者2处理完成!
消费者2接收到的消息work42
消费者2处理完成!
消费者2接收到的消息work44
消费者2处理完成!
消费者2接收到的消息work46
消费者2处理完成!
消费者2接收到的消息work48
消费者2处理完成!
消费者2接收到的消息work50
消费者2处理完成!

消费者1:

消费者2接收到的消息work2
消费者2处理完成!
消费者2接收到的消息work4
消费者2处理完成!
消费者2接收到的消息work6
消费者2处理完成!
消费者2接收到的消息work8
消费者2处理完成!
消费者2接收到的消息work10
消费者2处理完成!
消费者2接收到的消息work12
消费者2处理完成!
消费者2接收到的消息work14
消费者2处理完成!
消费者2接收到的消息work16
消费者2处理完成!
消费者2接收到的消息work18
消费者2处理完成!
消费者2接收到的消息work20
消费者2处理完成!
消费者2接收到的消息work22
消费者2处理完成!
消费者2接收到的消息work24
消费者2处理完成!
消费者2接收到的消息work26
消费者2处理完成!
消费者2接收到的消息work28
消费者2处理完成!
消费者2接收到的消息work30
消费者2处理完成!
消费者2接收到的消息work32
消费者2处理完成!
消费者2接收到的消息work34
消费者2处理完成!
消费者2接收到的消息work36
消费者2处理完成!
消费者2接收到的消息work38
消费者2处理完成!
消费者2接收到的消息work40
消费者2处理完成!
消费者2接收到的消息work42
消费者2处理完成!
消费者2接收到的消息work44
消费者2处理完成!
消费者2接收到的消息work46
消费者2处理完成!
消费者2接收到的消息work48
消费者2处理完成!
消费者2接收到的消息work50
消费者2处理完成!
测试结果:
1.消费者 1 和消费者 2 获取到的消息内容是不同的,同一个消息只能被一个消费者获取
2.消费者 1 和消费者 2 货到的消息数量是一样的 一个奇数一个偶数
按道理消费者 1 获取的比消费者 2 要多
 
这种方式叫做轮询分发 结果就是不管谁忙或清闲,都不会给谁多一个任务或少一个任务,
任务总是你一个我一个的分

官方文档的解释如下:

3、代码实例(公平派遣/公平分发)Fair dispatch

您可能已经注意到调度仍然无法完全按照我们的意愿运行。例如,在有两个工人的情况下,当所有奇怪的消息都
很重,甚至消息很轻时,一个工人将经常忙,而另一个工作人员几乎不会做任何工作。那么,RabbitMQ对此一无
所知,仍然会均匀地发送消息。
 
发生这种情况是因为RabbitMQ只是在消息进入队列时调度消息。它不会查看消费者未确认消息的数量。它只是盲
目地向第n个消费者发送每个第n个消息。

我们可以使用basicQos方法和 prefetchCount = 1设置。这告诉RabbitMQ一次不向
一个worker发送一条消息。或者,换句话说,在处理并确认前一个消息之前,不要
向工作人员发送新消息。相反,它会将它发送给下一个仍然很忙的工人。

还有一点需要注意,使用公平分发,必须关闭自动应答,改为手动应答

总结:能者多劳

生产者:

import java.io.IOException;
import java.util.concurrent.TimeoutException; import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.util.ConnectionUtils; public class Send { private static final String QUEUE_NAME ="test_work_queue"; public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//获取连接
Connection conn = ConnectionUtils.getConnection(); //获取Channel
Channel channel= conn.createChannel();
//声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false,null); //每个消费者发送确认消息之前,消费队列不发送下一个消息到消费者,一个只处理一个消息
//限制发送给同一个消费者不得超过一个消息
int prefetchCount = 1;
channel.basicQos(prefetchCount );
//连续发送50个消息
for(int i = ; i<=;i++){
String msg = "work" + i;
channel.basicPublish("",QUEUE_NAME, null, msg.getBytes());
Thread.sleep(i + );
}
channel.close();
conn.close();
} }

消费者1:

import java.io.IOException;
import java.util.concurrent.TimeoutException; import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.util.ConnectionUtils; public class WorkReceive { private static final String QUEUE_NAME ="test_work_queue"; public static void main(String[] args) throws IOException, TimeoutException { Connection conn = ConnectionUtils.getConnection(); Channel channel = conn.createChannel();
//声明队列
channel.queueDeclareNoWait(QUEUE_NAME, false, false, false, null); //保证一次只分发一次
int prefetchCount = 1;
channel.basicQos(prefetchCount );
//定义一个消费者
Consumer consumer = new DefaultConsumer(channel){
//收到消息就会触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String msg = new String(body,"utf-8");
System.out.println("消费者1接收到的消息" + msg); try {
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
System.out.println("消费者1处理完成!");
//手动回执
channel.basicAck(envelope.getDeliveryTag(), false
);
} }
};
//监听队列
//自动应答false
boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}

消费者2:

import java.io.IOException;
import java.util.concurrent.TimeoutException; import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.util.ConnectionUtils; public class WorkReceive2 { private static final String QUEUE_NAME ="test_work_queue"; public static void main(String[] args) throws IOException, TimeoutException { Connection conn = ConnectionUtils.getConnection(); Channel channel = conn.createChannel();
//声明队列
channel.queueDeclareNoWait(QUEUE_NAME, false, false, false, null); int prefetchCount = 1;
channel.basicQos(prefetchCount );
//定义一个消费者
Consumer consumer = new DefaultConsumer(channel){
//收到消息就会触发这个方法
@Override
public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body)
throws IOException {
String msg = new String(body,"utf-8");
System.out.println("消费者2接收到的消息" + msg); try {
Thread.sleep();
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
System.out.println("消费者2处理完成!");
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
//监听队列
boolean autoAck = false
;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);
}
}

消费者1:

消费者1接收到的消息work1
消费者1处理完成!
消费者1接收到的消息work4
消费者1处理完成!
消费者1接收到的消息work6
消费者1处理完成!
消费者1接收到的消息work9
消费者1处理完成!
消费者1接收到的消息work11
消费者1处理完成!
消费者1接收到的消息work14
消费者1处理完成!
消费者1接收到的消息work16
消费者1处理完成!
消费者1接收到的消息work19
消费者1处理完成!
消费者1接收到的消息work21
消费者1处理完成!
消费者1接收到的消息work24
消费者1处理完成!
消费者1接收到的消息work26
消费者1处理完成!
消费者1接收到的消息work29
消费者1处理完成!
消费者1接收到的消息work31
消费者1处理完成!
消费者1接收到的消息work34
消费者1处理完成!
消费者1接收到的消息work36
消费者1处理完成!
消费者1接收到的消息work39
消费者1处理完成!
消费者1接收到的消息work41
消费者1处理完成!
消费者1接收到的消息work44
消费者1处理完成!
消费者1接收到的消息work46
消费者1处理完成!
消费者1接收到的消息work49
消费者1处理完成!

消费者2:

消费者2接收到的消息work2
消费者2处理完成!
消费者2接收到的消息work3
消费者2处理完成!
消费者2接收到的消息work5
消费者2处理完成!
消费者2接收到的消息work7
消费者2处理完成!
消费者2接收到的消息work8
消费者2处理完成!
消费者2接收到的消息work10
消费者2处理完成!
消费者2接收到的消息work12
消费者2处理完成!
消费者2接收到的消息work13
消费者2处理完成!
消费者2接收到的消息work15
消费者2处理完成!
消费者2接收到的消息work17
消费者2处理完成!
消费者2接收到的消息work18
消费者2处理完成!
消费者2接收到的消息work20
消费者2处理完成!
消费者2接收到的消息work22
消费者2处理完成!
消费者2接收到的消息work23
消费者2处理完成!
消费者2接收到的消息work25
消费者2处理完成!
消费者2接收到的消息work27
消费者2处理完成!
消费者2接收到的消息work28
消费者2处理完成!
消费者2接收到的消息work30
消费者2处理完成!
消费者2接收到的消息work32
消费者2处理完成!
消费者2接收到的消息work33
消费者2处理完成!
消费者2接收到的消息work35
消费者2处理完成!
消费者2接收到的消息work37
消费者2处理完成!
消费者2接收到的消息work38
消费者2处理完成!
消费者2接收到的消息work40
消费者2处理完成!
消费者2接收到的消息work42
消费者2处理完成!
消费者2接收到的消息work43
消费者2处理完成!
消费者2接收到的消息work45
消费者2处理完成!
消费者2接收到的消息work47
消费者2处理完成!
消费者2接收到的消息work48
消费者2处理完成!
消费者2接收到的消息work50
消费者2处理完成!

这时候现象就是消费者 1 速度小于消费者 2

体现一句话:能者多劳

3、RabbitMQ-work queues 工作队列的更多相关文章

  1. RabbitMQ --- Work Queues(工作队列)

    目录 RabbitMQ --- Hello Mr.Tua 前言 Work Queues 即工作队列,它表示一个 Producer 对应多个 Consumer,包括两种分发模式:轮循分发(Round-r ...

  2. 译:2. RabbitMQ Java Client 之 Work Queues (工作队列)

    在上篇揭开RabbitMQ的神秘面纱一文中,我们编写了程序来发送和接收来自命名队列的消息. 本篇我们将创建一个工作队列,工作队列背后的假设是每个任务都交付给一个工作者 本篇是译文,英文原文请移步:ht ...

  3. rabbitmq消息队列——"工作队列"

    二."工作队列" 在第一节中我们发送接收消息直接从队列中进行.这节中我们会创建一个工作队列来分发处理多个工作者中的耗时性任务. 工作队列主要是为了避免进行一些必须同步等待的资源密集 ...

  4. RabbitMQ入门教程——工作队列

    什么是工作队列 工作队列是为了避免等待一些占用大量资源或时间操作的一种处理方式.我们把任务封装为消息发送到队列中,消费者在后台不停的取出任务并且执行.当运行了多个消费者工作进程时,队列中的任务将会在每 ...

  5. RabbitMQ入门:工作队列(Work Queue)

    在上一篇博客<RabbitMQ入门:Hello RabbitMQ 代码实例>中,我们通过指定的队列发送和接收消息,代码还算是比较简单的. 假设有这一些比较耗时的任务,按照上一次的那种方式, ...

  6. RabbitMQ入门(2)——工作队列

    前面介绍了队列接收和发送消息,这篇将学习如何创建一个工作队列来处理在多个消费者之间分配耗时的任务.工作队列(work queue),又称任务队列(task queue). 工作队列的目的是为了避免立刻 ...

  7. RabbitMQ 之 WorkQueues工作队列

    模型图 为什么会出现 work queues? 前提:使用 simple 队列的时候 (上一篇博客)我们应用程序在是使用消息系统的时候,一般生产者 P 生产消息是毫不费力的(发送消息即可),而消费者接 ...

  8. (转) RabbitMQ学习之工作队列(java)

    http://blog.csdn.net/zhu_tianwei/article/details/40887717 参考:http://blog.csdn.NET/lmj623565791/artic ...

  9. 译: 2. RabbitMQ Spring AMQP 之 Work Queues

    在上一篇博文中,我们写了程序来发送和接受消息从一个队列中. 在这篇博文中我们将创建一个工作队列,用于在多个工作人员之间分配耗时的任务. Work Queues 工作队列(又称:任务队列)背后的主要思想 ...

随机推荐

  1. Echarts 有点难度的柱状图

    本次的难点在于交叉传数据,又要把四组20个不同日期 显示上! 先看效果图: 数据传递方式:图 function func_echarts_2ba() { var echarts_2bar = echa ...

  2. 给<input>文本框添加灰色提示文字

    value="你的提示文字" onFocus="if(value==defaultValue){value='';this.style.color='#000'}&quo ...

  3. JAVA基础之——JDK分析io、nio

    在哪儿:jdk\jre\lib\rt.jar package java.io;   package java.nio; 1 分类 1.1 IO 持久化序列化对象并压缩步骤 new FileOutput ...

  4. 【SSH网上商城项目实战15】线程、定时器同步首页数据(类似于博客定期更新排名)

    转自:https://blog.csdn.net/eson_15/article/details/51387378 上一节我们做完了首页UI界面,但是有个问题:如果我在后台添加了一个商品,那么我必须重 ...

  5. Spring入门(二)— IOC注解、Spring测试、AOP入门

    一.Spring整合Servlet背后的细节 1. 为什么要在web.xml中配置listener <listener> <listener-class>org.springf ...

  6. Dubbo(二) 一次惨痛的流血事故

    时间定位到2018年11月某某一天,我正在看看Spring源码的文档,趁着没啥事,忽然想起Dubbo是基于Schema扩展的,所以就翻了下Dubbo的源码. 然后的然后,有活要干了,写完代码后,启动工 ...

  7. Oracle数据库采用数据泵方式导入导出数据

    特别说明:Oralce的数据泵导入导出技术只能用在数据库服务器上,在只有客户端的机器上是无法使用数据泵技术的. 1.创建备份文件目录  mkdir d:\dmp 2.在Oralce中注册该目录,将目录 ...

  8. 前端学习之路之CSS (四)

    Infi-chu: http://www.cnblogs.com/Infi-chu/ CSS盒子模型    概念:CSS盒模型本质上是一个盒子,封装周围的HTML元素,它包括:边距,边框,填充,和实际 ...

  9. div实现水平和垂直都居中的三个超实用的方法

    本文仅仅介绍作者认为的三种不错的方式, 方式一:transform: translate(-50%,-50%)  示例代码如下: .div{ position: absolute; top: 50%; ...

  10. DrawerLayout使用

    1 :DrawerLayout侧边栏用于实现如图所示的效果: