工作队列:把每个任务只发送给一个工作者。

上一篇我们是从一个指定的队列发送接收消息,在本文中,我们将创建一个工作队列,用于在多个工作者之间分配耗时的任务。

工作队列(即任务队列)背后的主要思想是避免立即执行资源密集型的任务,并且必须等待任务完成。相反,我们把任务安排在以后做。我们将任务封装为消息并将其发送到队列。在后台运行的worker进程将弹出任务并最终执行任务。当您运行多个worker时,这些任务将在它们之间共享。

因为工作队列,是有多个工人从队列里面取得任务,我们就需要考虑较多的问题,比如说,消息怎么分发,消息没有传递到位如何,消息传递过程中,连接断开如何,消费会不会重复发给两个工人处理等等。下面我们先学习一些跟工作队列相关的概念:

循环调度

 

使用任务队列的优点之一是能够轻松地并行工作。如果我们正在建立一个积压的工作,我们可以增加更多的工人,这样,规模就很容易。

RabbitMQ将按顺序将每个消息发送给下一个使用者。平均来说,每个消费者都会收到相同数量的信息。这种分发消息的方式称为循环。

消息确认

 

完成一项任务可能需要几秒钟。你可能会想,如果其中一个消费者开始了一项很长的任务,并且只完成了部分任务,会发生什么。使用我们当前的代码,一旦RabbitMQ向客户传递消息,它立即标记为删除。在这种情况下,如果你杀了一个工人,我们将失去它正在处理的信息。我们还将丢失已经发送给此特定工作人员但尚未处理的所有消息。

 

但我们不想失去任何任务。如果一个工作者死了,我们希望把任务交给另一个工作者。

为了确保消息不会丢失,RabbitMQ支持消息确认。使用者将一个ack(nowledgement)发回给RabbitMQ,告诉它已经接收、处理了一个特定的消息,并且RabbitMQ可以自由地删除它。

如果使用者在没有发送ack的情况下死亡(通道关闭、连接关闭或TCP连接丢失),RabbitMQ将理解消息没有被完整地处理,并将它重新排队。如果同时有其他消费者在线,它将很快重新交付给另一个消费者。这样你就可以确保没有信息丢失,即使工人偶尔也会死亡。

消息的持久性

 

我们已经学会了如何确保即使消费者死亡,任务也不会丢失。但是如果RabbitMQ服务器停止,我们的任务仍然会丢失。

 

当RabbitMQ退出或崩溃时,它将忘记队列和消息,除非您告诉它不要这样做。确保消息不丢失需要两件事情:我们需要将队列和消息标记为持久的。

 

首先,我们需要确保RabbitMQ永远不会丢失队列。为了做到这一点,我们需要声明它是持久的.

消息的公平分发机制

为了避免当消息分发后,有的工人非常忙,而有的很闲的问题,我们可以使用basicQos方法,并将prefetchCount = 1设置为。这告诉RabbitMQ不要一次向工作人员发送多个消息。或者,换句话说,在处理并确认之前的消息之前,不要向工作人员发送新的消息。相反,它将把它分派给下一个不太忙的员工。

上代码:

生产者:

package com.rabbitmq.HelloWorld;

import java.io.IOException;
import java.util.concurrent.TimeoutException; import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties; public class WorkQueueProduct {
private final static String TASK_QUEUE_NAME = "task"; public static void main(String[] args) throws IOException, TimeoutException {
// TODO Auto-generated method stub
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.10.185");
factory.setUsername("admin");
factory.setPassword("123456");
factory.setPort(5672);
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
// 设置队列为可持久性的(生产者和消费者都需要设置)注意:RabbitMQ不允许您重新定义具有不同参数的现有队列
channel.queueDeclare(TASK_QUEUE_NAME, true, false, false, null);
String[] str = {"ans1","b","c","d","e","f","g","h","1","2","3","4","5"};
String message = getMessage(str);
// 将消息设置为持久性,设置消息的其他属性为MessageProperties.PERSISTENT_TEXT_PLAIN,即可
channel.basicPublish("", TASK_QUEUE_NAME, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes("utf-8"));
System.out.println("[x]send:'"+message+"'");
channel.close();
connection.close(); }
private static String getMessage(String[] strings) {
if (strings.length < 1)
return "Hello World!";
return joinStrings(strings, " ");
} private static String joinStrings(String[] strings, String delimiter) {
int length = strings.length;
if (length == 0) return "";
StringBuilder words = new StringBuilder(strings[0]);
for (int i = 1; i < length; i++) {
words.append(delimiter).append(strings[i]);
}
return words.toString();
}
}

消费者:两个消费者代码相同

package com.rabbitmq.HelloWorld;

import java.io.IOException;
import java.util.concurrent.TimeoutException; import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.Consumer;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties; public class WorkQueueConsumer1 {
private static final String TASK_QUEUE_NAME = "task";
public static void main(String[] args) throws IOException, TimeoutException {
// TODO Auto-generated method stub
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("192.168.10.185");
factory.setUsername("admin");
factory.setPassword("123456");
factory.setPort(5672);
Connection connction = factory.newConnection();
Channel channel = connction.createChannel();
channel.queueDeclare(TASK_QUEUE_NAME, true, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C");
// 设置公平分发机制参数,设置为1后每次只发送一个消息,并且在没有发送确认消息之前不会再次发送消息
channel.basicQos(1);
// 内置回调函数,处理消息
final Consumer consumer = new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope,
BasicProperties properties, byte[] body) throws IOException {
// TODO Auto-generated method stub
String message = new String(body, "utf-8");
System.out.println(" [x] Received '" + message + "'");
try {
dowork(message);
} catch (Exception e) {
// TODO: handle exception
}finally{
System.out.println(" [x] Done");
// 任务处理完之后的消息确认
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
channel.basicConsume(TASK_QUEUE_NAME, false,consumer);
}
private static void dowork(String task) {
// TODO Auto-generated method stub
for(char c :task.toCharArray()){
if(c == '.'){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
Thread.currentThread().interrupt();
}
}
} } }

官网英文版学习——RabbitMQ学习笔记(四)Work queues的更多相关文章

  1. 官网英文版学习——RabbitMQ学习笔记(一)认识RabbitMQ

    鉴于目前中文的RabbitMQ教程很缺,本博主虽然买了一本rabbitMQ的书,遗憾的是该书的代码用的不是java语言,看起来也有些不爽,且网友们不同人学习所写不同,本博主看的有些地方不太理想,为此本 ...

  2. 官网英文版学习——RabbitMQ学习笔记(十)RabbitMQ集群

    在第二节我们进行了RabbitMQ的安装,现在我们就RabbitMQ进行集群的搭建进行学习,参考官网地址是:http://www.rabbitmq.com/clustering.html 首先我们来看 ...

  3. Unity shader 官网文档全方位学习(一)

    转载:https://my.oschina.net/u/138823/blog/181131 摘要: 这篇文章主要介绍Surface Shaders基础及Examples详尽解析 What?? Sha ...

  4. 利用JQ实现的,高仿 彩虹岛官网导航栏(学习HTML过程中的小记录)

    利用JQ实现的,高仿 彩虹岛官网导航栏(学习HTML过程中的小记录)   作者:王可利(Star·星星) 总结: 今天学习的jQ类库的使用,代码重复的比较多需要完善.严格区分大小写,在 $(" ...

  5. 官网英文版学习——RabbitMQ学习笔记(二)RabbitMQ安装

    一.安装RabbitMQ的依赖Erlang 要进行RabbitMQ学习,首先需要进行RabbitMQ服务的安装,安装我们可以根据官网指导进行http://www.rabbitmq.com/downlo ...

  6. 官网英文版学习——RabbitMQ学习笔记(三)Hello World!

    参考http://www.rabbitmq.com/tutorials/tutorial-one-java.html,我们直接上代码,由于我们的RabbitMQ服务是安装在虚拟机上的,具体参考上一节. ...

  7. 【官网翻译】性能篇(四)为电池寿命做优化——使用Battery Historian分析电源使用情况

    前言 本文翻译自“为电池寿命做优化”系列文档中的其中一篇,用于介绍如何使用Battery Historian分析电源使用情况. 中国版官网原文地址为:https://developer.android ...

  8. 官网英文版学习——RabbitMQ学习笔记(八)Remote procedure call (RPC)

    在第四篇学习笔记中,我们学习了如何使用工作队列在多个工作者之间分配耗时的任务.   但是,如果我们需要在远程计算机上运行一个函数并等待结果呢?这是另一回事.这种模式通常称为远程过程调用或RPC.   ...

  9. 官网英文版学习——RabbitMQ学习笔记(七)Topic

    在上一篇中使用直接交换器改进了我们的系统,使得它能够有选择的进行接收消息,但它仍然有局限性——它不能基于多个条件进行路由.本节我们就进行能够基于多个条件进行路由的topics exchange学习. ...

随机推荐

  1. python随机函数.2020.2.26

    随机生成函数: import random  //首先要引用random模板 print(random.randint(0,9))     //random的语法 random.randint(0,9 ...

  2. CentOS7中下载安装Multitail(让你的日志文件变得多彩)

    MultiTail是干啥的? Linux系统下查看日志的一个工具,允许您监视终端中多个窗口中的日志文件和命令输出,着色,过滤和合并. 具体介绍请看官网:https://www.vanheusden.c ...

  3. Docker如何使用nginx搭建tomcat集群

    首先创建tomcat的文件夹 , 为了方便docker的配置 我这里直接在根目录中创建 第一步:创建文件夹:发布文件夹 mkdir -p /docker/tomcat/webapp8081 mkdir ...

  4. 使用mybase、Typora搭配坚果云实现个人云笔记

    如果我们没有使用印象笔记.有道云之类的云笔记,那么就会遇到一个问题,比如我在公司是用的公司的电脑,然后下班回家用的自己的电脑,那么我在公司写的文档,比如markdown 文件,mybase知识管理工具 ...

  5. 修改gho打造个性ghostxp光盘

    一.更换OEM信息(更改用户名.公司名与电脑名的方法 1.用软碟通提取出ghostxp中的gho镜像文件. 2.,用GHOSTEXP .EXE打开这个GHO文件,提取sysprep目录下的syspre ...

  6. 吴裕雄 Bootstrap 前端框架开发——Bootstrap 按钮:禁用按钮

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  7. 初识Prometheus

    安装Prometheus Server Prometheus基于Golang编写,编译后的软件包,不依赖于任何的第三方依赖.用户只需要下载对应平台的二进制包,解压并且添加基本的配置即可正常启动Prom ...

  8. 新闻网大数据实时分析可视化系统项目——8、Flume数据采集准备

    Flume是Cloudera提供的一个高可用的,高可靠的,分布式的海量日志采集.聚合和传输的系统,Flume支持在日志系统中定制各类数据发送方,用于收集数据:同时,Flume提供对数据进行简单处理,并 ...

  9. RTL级低功耗设计

    重点:门控时钟和操作数隔离 1.并行与流水的选择 并行与流水都是用面积换速度,但是有时可以降低功耗 并行处理常用于数字信号处理部分,采用并行处理,也已降低系统工作频率从而降低功耗 用两个乘法器取代原设 ...

  10. jumperserver安装

    参照官网地址进行安装 https://jumpserver.readthedocs.io/zh/master/setup_by_centos.html 在安装的时候踩了一个坑 Python 模块安装中 ...