在上一篇博文中,我们写了程序来发送和接受消息从一个队列中。

在这篇博文中我们将创建一个工作队列,用于在多个工作人员之间分配耗时的任务。

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

这个概念在Web应用程序中特别有用,因为在短的HTTP请求窗口中无法处理复杂的任务。

我们没有真实的业务场景,因此接下来我们将会用Thread.sleep()方法来模拟一个耗时比较久的任务。

编写application.properties

我们将在生成的项目中找到application.properties文件,其中没有任何内容。

添加application.properties 配置如下:

spring.profiles.active=usage_message
logging.level.org=ERROR
tutorial.client.duration=10000
# 当declareExchange为true时,将持久标志设置为此值
spring.rabbitmq.durable=true
# PERSISTENT或NON_PERSISTENT确定RabbitMQ是否应该保留消息
spring.rabbitmq.deliveryMode=PERSISTENT
# 更多属性设置 https://docs.spring.io/spring-amqp/reference/htmlsingle/#_common_properties

编写Java配置类

刚才配置文件中我们配置了一个

tutorial.client.duration=10000

但是这个配置字段不存在于任何框架jar包里,因此我们需要编写一个类来处理这个属性

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ConfigurableApplicationContext; public class RabbitAmqpTutorialsRunner implements CommandLineRunner { @Value("${tutorial.client.duration:0}")
private int duration; @Autowired
private ConfigurableApplicationContext ctx; @Override
public void run(String... args) throws Exception {
// TODO Auto-generated method stub
System.out.println("Ready ... running for " + duration + "ms");
Thread.sleep(duration);
ctx.close();
} }

我们仍然和之前教程一样需要一个Java配置类:

import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile; import com.xingyun.springamqp.business.Tut2Receiver;
import com.xingyun.springamqp.business.Tut2Sender; @Profile({"tut2", "work-queues"})
@Configuration
public class Tut2Config { @Bean
public Queue hello() {
return new Queue("hello");
} @Profile("receiver")
private static class ReceiverConfig { @Bean
public Tut2Receiver receiver1() {
return new Tut2Receiver(1);
} @Bean
public Tut2Receiver receiver2() {
return new Tut2Receiver(2);
}
} @Profile("sender")
@Bean
public Tut2Sender sender() {
return new Tut2Sender();
}
}

通过上面这个配置类,我们做了四件事

  1. 首先通过 @Profile 注解,定义了 两个配置文件前缀别名,tut2 或者 work-queues
  2. 通过@Configuration 注解来让Spring 知道这是一个Java 配置文件
  3. 定义了 一个队列,名字叫做hello
  4. 另外定义了两个配置文件,一个叫做sender,一个叫做receiver

为什么要有这两个配置文件? 因为我们待会运行生产者和消费者的时候,可以通过动态加载不同的配置文件来启动不同的类。

比如我们启动生产者发布信息就可以调用这个配置:

--spring.profiles.active=tut2,sender
当我们想启动消费者就动态调用这个配置
--spring.profiles.active=tut2,receiver

接下来我们需要修改下整个应用程序的启动类:

import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Profile;
import org.springframework.scheduling.annotation.EnableScheduling; import com.xingyun.springamqp.config.RabbitAmqpTutorialsRunner; @SpringBootApplication
@EnableScheduling
public class RabbitMq0x02SpringAmqpWorkQueuesSampleApplication { public static void main(String[] args) {
SpringApplication.run(RabbitMq0x02SpringAmqpWorkQueuesSampleApplication.class, args);
} @Profile("usage_message")
@Bean
public CommandLineRunner usage() {
return new CommandLineRunner() { @Override
public void run(String... arg0) throws Exception {
System.out.println("This app uses Spring Profiles to control its behavior.\n");
System.out.println("Sample usage: java -jar "
+ "RabbitMQ_0x02_SpringAMQP_WorkQueues_Sample-0.0.1-SNAPSHOT.jar "
+ "--spring.profiles.active=work-queues,sender");
}
};
} @Profile("!usage_message")
@Bean
public CommandLineRunner tutorial() {
return new RabbitAmqpTutorialsRunner();
}
}

当执行这个项目的jar 文件时会自动加载这个usage_message 配置,打印用法信息。

我们在启动类上添加@EnableScheduling,以便于开启对定时任务的支持.

生产者

我们将修改发送方以通过在RabbitTemplate上使用相同的方法发布消息convertAndSend,以非常人为的方式在消息中附加一个点来提供识别其是否为更长时间运行任务的方法。该文档将此定义为“将Java对象转换为Amqp消息并将其发送到具有默认路由密钥的默认交换”。

import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled; public class Tut2Sender { @Autowired
private RabbitTemplate template; @Autowired
private Queue queue; int dots = 0;
int count = 0; @Scheduled(fixedDelay = 1000, initialDelay = 500)
public void send() {
StringBuilder builder = new StringBuilder("Hello");
if (dots++ == 3) {
dots = 1;
}
for (int i = 0; i < dots; i++) {
builder.append('.');
} builder.append(Integer.toString(++count));
String message = builder.toString();
template.convertAndSend(queue.getName(), message);
System.out.println(" [x] Sent '" + message + "'");
}
}

消费者

我们的接收器Tut2Receiver模拟doWork()方法中伪造任务的任意长度,其中点数转换为工作所需的秒数。同样,我们利用“hello”队列上的@RabbitListener和@RabbitHandler来接收消息。消耗该消息的实例将添加到我们的监视器中,以显示处理消息的实例,消息和时间长度。

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.util.StopWatch; @RabbitListener(queues = "hello")
public class Tut2Receiver { private final int instance; public Tut2Receiver(int i) {
this.instance = i;
} @RabbitHandler
public void receive(String in) throws InterruptedException {
StopWatch watch = new StopWatch();
watch.start();
System.out.println("instance " + this.instance +
" [x] Received '" + in + "'");
doWork(in);
watch.stop();
System.out.println("instance " + this.instance +
" [x] Done in " + watch.getTotalTimeSeconds() + "s");
} private void doWork(String in) throws InterruptedException {
for (char ch : in.toCharArray()) {
if (ch == '.') {
Thread.sleep(1000);
}
}
}
}

查看用法

java -jar RabbitMQ_0x02_SpringAMQP_WorkQueues_Sample-0.0.1-SNAPSHOT.jar

运行生产者

java -jar RabbitMQ_0x02_SpringAMQP_WorkQueues_Sample-0.0.1-SNAPSHOT.jar --spring.profiles.active=work-queues,sender

运行消费者

java -jar RabbitMQ_0x02_SpringAMQP_WorkQueues_Sample-0.0.1-SNAPSHOT.jar --spring.profiles.active=work-queues,receiver

译: 2. RabbitMQ Spring AMQP 之 Work Queues的更多相关文章

  1. 译: 1. RabbitMQ Spring AMQP 之 Hello World

    本文是译文,原文请访问:http://www.rabbitmq.com/tutorials/tutorial-one-spring-amqp.html RabbitMQ 是一个Brocker (消息队 ...

  2. 译: 3. RabbitMQ Spring AMQP 之 Publish/Subscribe 发布和订阅

    在第一篇教程中,我们展示了如何使用start.spring.io来利用Spring Initializr创建一个具有RabbitMQ starter dependency的项目来创建spring-am ...

  3. 译: 6. RabbitMQ Spring AMQP 之 RPC

    Remote procedure call (RPC) 在第二篇教程中,我们学习了如何使用工作队列在多个工作人员之间分配耗时的任务. 但是如果我们需要在远程计算机上运行一个函数并等待结果呢?嗯,这是一 ...

  4. 译: 4. RabbitMQ Spring AMQP 之 Routing 路由

    在上一个教程中,我们构建了一个简单的fanout(扇出)交换.我们能够向许多接收者广播消息. 在本教程中,我们将为其添加一个功能 - 我们将只能订阅一部分消息.例如,我们将只能将消息指向感兴趣的特定颜 ...

  5. 译: 5. RabbitMQ Spring AMQP 之 Topic 主题

    在上一个教程中,我们提高了消息传递的灵活 我们使用direct交换而不是使用仅能够进行虚拟广播的fanout交换, 并且获得了基于路由key 有选择地接收消息的可能性. 虽然使用direct 交换改进 ...

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

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

  7. spring amqp rabbitmq fanout配置

    基于spring amqp rabbitmq fanout配置如下: 发布端 <rabbit:connection-factory id="rabbitConnectionFactor ...

  8. 深入剖析 RabbitMQ —— Spring 框架下实现 AMQP 高级消息队列协议

    前言 消息队列在现今数据量超大,并发量超高的系统中是十分常用的.本文将会对现时最常用到的几款消息队列框架 ActiveMQ.RabbitMQ.Kafka 进行分析对比.详细介绍 RabbitMQ 在 ...

  9. 消息中间件——RabbitMQ(九)RabbitMQ整合Spring AMQP实战!(全)

    前言 1. AMQP 核心组件 RabbitAdmin SpringAMQP声明 RabbitTemplate SimpleMessageListenerContainer MessageListen ...

随机推荐

  1. Storm通信机制(了解)

    Worker间的通信:经常需要通过网络跨节点进行,Storm使用ZeroMQ或Netty(0.9以后默认使用)作为进程间通信的消息框架. Worker进程内部通信:不同worker的thread通信使 ...

  2. 053 关于hive的存储格式

    1.存储格式 textfile rcfile orc parquet 2.存储方式 按行存储 ->textfile 按列存储 ->parquet 3.压缩比 4.存储textfile的原文 ...

  3. dict 知识汇总

    增: 1. copy 浅复制 2. setdefault (有就查询,没有就添加): 删: 3. clear:清除 4. pop :删除指定键值对 5. popitem :  随机删除一组键值对 改: ...

  4. macos 下安装virtualenv,virtualenvwrapper,然后在pycharm中正常配置方法日志

    1.安装virtualenv或virtualenvwrapper pip install virtualenv pip install virtualenvwraper 注意pip的版本号(查看 pi ...

  5. 微信小程序:一起玩连线,一个算法来搞定

    微信小程序:一起玩连线 游戏玩法 将相同颜色的结点连接在一起,连线之间不能交叉. 算法思想 转换为多个源点到达对应终点的路径问题,且路径之间不相交.按照dfs方式寻找两个结点路径,一条路径探索完之后, ...

  6. 13,EasyNetQ-错误条件

    在本节中,我们将看看任何消息系统中可能出现的各种错误情况,并查看EasyNetQ如何处理它们. 1,我的订阅服务死亡 你已经写了一个订阅了我的NewCustomerMessage的windows服务. ...

  7. 运行程序,解读this指向---case2

    片段1 var anum = 666; function funcTest1(){ var b = anum * 2; var anum = 6; var c = anum / 2; console. ...

  8. 深度学习中 batchnorm 层是咋回事?

    作者:Double_V_ 来源:CSDN 原文:https://blog.csdn.net/qq_25737169/article/details/79048516 版权声明:本文为博主原创文章,转载 ...

  9. Android Firebase Android google-cloud-tools

    Firebase 让不懂服务端的开发者也可以快速写出实时性的Web端和移动端应用. firebase的功能包括推送通知,云存储,活动监视,远程部署 针对国内三方推送,只能在国内使用,到了国外就不支持了 ...

  10. git 快照及分支

    分支介绍 分支就是科幻电影里面的平行宇宙,当你正在电脑前努力学习Git的时候,另一个你正在另一个平行宇宙里努力学习SVN. 如果两个平行宇宙互不干扰,那对现在的你也没啥影响.不过,在某个时间点,两个平 ...