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. nodejs应用离线安装部署、卸载

    公司写的文档,直接粘贴过来了: 本文档提供node应用一键安装.部署.卸载说明,包含对应脚本文件 默认版本:1.nodejs:v6.11.2.linux-x642.全局npm包:pm2.supervi ...

  2. web.xml文件初始化过程

    在使用各种框架后,有时会发现不了了错误处在哪里,了解Servlet的初始化过程(也可以说是web.xml的初始化吧),也许对你对于框架的理解与报错的原因理解会有帮助 context-param > ...

  3. C# 提高必备精品--你所需要的NET笔记

    一. 交换两个数据的值: 1.  //创建一个临时变量 //int temp; //temp = num1; ////用num2的值覆盖掉num1 //num1 = num2; ////将存储在临时变 ...

  4. 从MySQL到ORM(三):连接、存储过程和用户权限

    一.联结表 数据仍使用前文中的数据. 1.子查询 作为子查询的SELECT语句只能查询单个列.企图检索多个列将返回错误. -- 作为查询条件使用 -- 查看TNT2订单对应的客户ip(order表) ...

  5. [LeetCode]3Sum Closest题解

    3sum Closest: Given an array S of n integers, find three integers in S such that the sum is closest ...

  6. 自定义scoll样式

    使用伪类自定义scroll样式 效果: 代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8 ...

  7. qt5.6.3下使用firebird

    有人把firebird比作数据库界的瑞士军刀,想学习一下其在QT5.6中的使用,于是便开始了一场自己挖坑,自己埋的旅程. 环境说明:win7 64位+QT5.6 mingw4.9 32位(好像官网上也 ...

  8. Aizu2292 Common Palindromes

    题意 我也不知道哪里来的OJ \(vjudge\) 上的 给定两个字符串 \(S,T\) ,询问 \((i,j,k,l)\) 这样的四元组个数 使得 \(S[i,j],T[k,l]\) 是相等的回文串 ...

  9. BZOJP1096[ZJOI2007]仓库建设——solution

    Description L公司有N个工厂,由高到底分布在一座山上.如图所示,工厂1在山顶,工厂N在山脚.由于这座山处于高原内陆地区(干燥少雨),L公司一般把产品直接堆放在露天,以节省费用.突然有一天, ...

  10. Maven学习总结(三):修改从Maven中心仓库下载到本地的jar包的默认存储位置

    一:修改从Maven中心仓库下载到本地的jar包的默认存储位置 从Maven中心仓库下载到本地的jar包的默认存放在”${user.home}/.m2/repository”中,${user.home ...