这次我们试着实现这样一个小程序:

嗯,就是任务队列(task queue)。
不是将任务集中在一堆并一直等到所有任务一并完成为止,而是将每一个任务封装为一个消息,并将其发送到队列,后台的workers就从队列中分担工作。
web应用尤其喜欢这种处理方式,比如面对一个请求时我们有一大堆复杂逻辑需要处理,而我们却不需要立即响应处理结果,那就放到后面慢慢弄。
(PS:另外也有直接对任务进行持久化,然后用scheduler什么的去定时处理。无论如何,没有银弹。)

对于复杂的任务,我们可以用Thread.sleep模拟一下。
比如provider每发一个"hello...",worker读到消息后开始数点,每读到一个"."就睡一会儿。

provider也简单模拟一下,一次塞个20个消息到队列:

public static void main(String[] argv) throws java.io.IOException {

    ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel(); channel.queueDeclare(TASK_QUEUE_NAME, true, false, false, null); String message = "Hello..."; for (int i = 0; i < 20; i++) {
channel.basicPublish("", TASK_QUEUE_NAME,
MessageProperties.PERSISTENT_TEXT_PLAIN, message.concat(i+1+"").getBytes());
System.out.println(" [x] Sent '" + message + (i + 1) + "' "
+ (i + 1) + " times");
} channel.close();
connection.close();
}

有一个需要注意的地方,就是consumer揽了活后没干完就死掉了。
我需要其他还活着的consumer替死者完成工作。
RabbitMQ支持消息应答,如果worder没有做出应答却死掉了,provider则会将消息重新发给其他活着的consumer。
但这个和timeout无关,只有在worker的connection断掉时才会重新发送。

如果调用了没有autoAck参数的basicConsume,消息应答默认是启用的,也就是autoAck=false。

boolean autoAck = false;
channel.basicConsume(TASK_QUEUE_NAME, autoAck, consumer);

当autoAck==false时需要我们显示调用channel.basicAck方法将接收的消息ack一下。
如果接收了消息却不显示调用应答方法,就不能再接收新的消息,这就造成了浪费。
另外,如果设置了autoAck就不要显示进行应答,否则会来一个com.rabbitmq.client.ShutdownSignalException。

consumer死了有其他人处理后事,那整个server死掉了怎么办?
为了让消息不丢失,我们需要将队列和消息标记为durable。

boolean durable = true;
channel.queueDeclare("hello", durable, false, false, null);

好了,这样即使重启RabbitMQ服务也不会丢失队列。

但这并不保证消息不会丢失,为了保证这一点,我们在provider发布消息时加了essageProperties.PERSISTENT_TEXT_PLAIN:

channel.basicPublish("", TASK_QUEUE_NAME,
MessageProperties.PERSISTENT_TEXT_PLAIN, message.concat(i+1+"").getBytes());

虽然这种方式并不完美,我们还需要做其他的一些工作,但暂时先到这里。

最后一个问题是,如何做到给consumer公平分配任务。
如果没有做这个处理,会出现这样一种情况。
举个例子:provider发送了20个消息,随即启动的consumer_1把这20个消息全都独占了。
在consumer_1工作期间又有consumer_2被启动,但此时consumer_2没有任何任务。
此时provider又发送了20个消息,这时consumer_2会得到10个任务。

我们可以使用channel.basicQos(int prefetchCount)方法限制预获取的数量,比如prefetchCount==1就是返回应答后可以再获得1个消息。

好了,consumer代码如下:

public class Worker {
private static final String TASK_QUEUE_NAME = "task_queue"; public static void main(String[] argv) throws java.io.IOException,
java.lang.InterruptedException { ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel(); channel.queueDeclare(TASK_QUEUE_NAME, true, false, false, null);
System.out.println(" [*] Waiting for messages. To exit press CTRL+C"); channel.basicQos(1); QueueingConsumer consumer = new QueueingConsumer(channel);
boolean autoAck = false;
channel.basicConsume(TASK_QUEUE_NAME, autoAck, consumer); while (true) {
QueueingConsumer.Delivery delivery = consumer.nextDelivery();
String message = new String(delivery.getBody()); System.out.println(" [x] Received '" + message + "'");
doWork(message);
System.out.println(" [x] Done");
channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);
}
} private static void doWork(String task) throws InterruptedException {
for (char ch : task.toCharArray()) {
if (ch == '.')
Thread.sleep(1000);
}
} }

RabbitMQ - 任务队列的更多相关文章

  1. 在Node.js中使用RabbitMQ系列二 任务队列

    在上一篇文章在Node.js中使用RabbitMQ系列一 Hello world我有使用一个任务队列,不过当时的场景是将消息发送给一个消费者,本篇文章我将讨论有多个消费者的场景. 其实,任务队列最核心 ...

  2. python之celery的使用(一)

    前段时间需要使用rabbitmq做写缓存,一直使用pika+rabbitmq的组合,pika这个模块虽然可以很直观地操作rabbitmq,但是官方给的例子太简单,对其底层原理了解又不是很深,遇到很多坑 ...

  3. Celery异步任务队列/周期任务+ RabbitMQ + Django

    一.Celery介绍和基本使用  Celery 是一个 基于python开发的分布式异步消息任务队列,通过它可以轻松的实现任务的异步处理, 如果你的业务场景中需要用到异步任务,就可以考虑使用celer ...

  4. RabbitMQ之任务队列【译】

    在第一个教程里面,我们写了一个程序从一个有名字的队列中发送和接收消息,在这里我们将要创建一个分发耗时任务给多个worker的任务队列. 任务队列核心思想就是避免执行一个资源密集型的任务,而程序要等待其 ...

  5. Scrapy使用RabbitMQ做任务队列

    前言 一个月没更博客了,这个月也搞了不少东西,但是公司对保密性要求挺高,很多东西都没有办法写出来 想来想去,还是写一篇最近写Scrapy中遇到的跳转问题 如果你的业务需求是遇到301/302/303跳 ...

  6. RabbitMQ Go客户端教程2——任务队列/工作队列

    本文翻译自RabbitMQ官网的Go语言客户端系列教程,本文首发于我的个人博客:liwenzhou.com,教程共分为六篇,本文是第二篇--任务队列. 这些教程涵盖了使用RabbitMQ创建消息传递应 ...

  7. 异步任务队列Celery在Django中的使用

    前段时间在Django Web平台开发中,碰到一些请求执行的任务时间较长(几分钟),为了加快用户的响应时间,因此决定采用异步任务的方式在后台执行这些任务.在同事的指引下接触了Celery这个异步任务队 ...

  8. .NET 环境中使用RabbitMQ

    在企业应用系统领域,会面对不同系统之间的通信.集成与整合,尤其当面临异构系统时,这种分布式的调用与通信变得越发重要.其次,系统中一般会有很多对实时性要求不高的但是执行起来比较较耗时的地方,比如发送短信 ...

  9. C# RabbitMq .net 使用

    本文转载来自 [http://www.cnblogs.com/yangecnu/p/Introduce-RabbitMQ.html]写的很详细. 文件安装包官方DEMO下载地址是:http://pan ...

随机推荐

  1. 初学python - 使用pip安装扩展库

    cmd pip install -i https://pypi.tuna.tsinghua.edu.cn/simple PyMySQL - 使用清华镜像下载PyMySQL pip python包管理工 ...

  2. Linux下iptables防火墙用法规则详解

    管理网络流量是系统管理员必需处理的最棘手工作之一,我们必需规定连接系统的用户满足防火墙的传入和传出要求,以最大限度保证系统免受×××.很多用户把 Linux 中的iptables当成一个防火墙,从严格 ...

  3. “全栈2019”Java第八十四章:接口中嵌套接口详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  4. POJ1475 Pushing Boxes(双搜索)

    POJ1475 Pushing Boxes  推箱子,#表示墙,B表示箱子的起点,T表示箱子的目标位置,S表示人的起点 本题没有 Special Judge,多解时,先最小化箱子被推动的次数,再最小化 ...

  5. 最短路 CF954D Fight Against Traffic

    CF954D Fight Against Traffic 题意描述: 给你一张无向图,一共有n个点(2 <= n <= 1000),由m条边连接起来(1 <= m <= 100 ...

  6. 网络传输层之TCP/UDP详解

    一.运输层协议概述 从通信和信息处理的角度看,运输层向它上面的应用层提供通信服务,它属于面向通信部分的最高层,同时也是用户功能中的最低层. 运输层的任务就是负责主机中两个进程之间的通信,其数据传输的单 ...

  7. Linux新手随手笔记1.9-使用Apache搭建网站

    搭建网站 网站服务:让用户能够通过浏览器访问到的服务器上的文档资源. 对比Windows 和Linux部署服务方法对比 Windows:IIS Linux      :Apache ,nginx Ap ...

  8. 基础篇:6.4)形位公差-基准 Datum

    本章目标:了解形位公差基准及运用. 1.定义: 基准 —  与被测要素有关且用来定义其几何位置关系的一个几何理想要素(如轴线.直线.平面等): —  可由零件上的一个或多个基准要素构成. 模拟基准要素 ...

  9. Java返回泛型的方法-实例

    package com.frank.app; import java.io.UnsupportedEncodingException;import java.lang.reflect.Type;imp ...

  10. nginx常用配置3

    ## 六.浏览器本地缓存配置 语法:expires 60 s|m|h|d ```动静分离效果: server { listen 80; server_name localhost; location ...