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

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

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. 3.Django| 视图层| 模板层

    1.视图函数 文件在view_demo 一个视图函数简称视图,是一个简单的Python 函数,它接受Web请求并且返回Web响应.响应可以是一张网页的HTML内容,一个重定向,一个404错误,一个XM ...

  2. Linux 之 AT&T汇编语言 mov、add、sub指令、数据段

    mov指令的几种形式: mov 寄存器. 数据 mov ax,8888 mov 寄存器. 寄存器 mov bx,ax mov 寄存器. 内存单元 mov ax,[0] mov 内存单元.寄存器 mov ...

  3. SpringBoot+Mybatis多模块(module)项目搭建教程

    一.前言 最近公司项目准备开始重构,框架选定为SpringBoot+Mybatis,本篇主要记录了在IDEA中搭建SpringBoot多模块项目的过程. 1.开发工具及系统环境 IDE:Intelli ...

  4. ios技术篇:导航栏push遵循的三个规则

    1.如果B视图有一个自定义的左侧按钮(leftBarButtonItem),则会显示这个自定义按钮 2.如果B没有自定义按钮,但是A视图的backBarButtonItem属性有自定义项,则显示这个自 ...

  5. 11.7 NOIP模拟赛

    目录 2018.11.7 NOIP模拟 A 序列sequence(two pointers) B 锁lock(思路) C 正方形square(埃氏筛) 考试代码 B C 2018.11.7 NOIP模 ...

  6. 命令行方式删除文件 && 文件夹

    del File: 删除文件 rmdir Folder : 删除文件夹 [貌似只能删除空目录] rm -rf Folder: 删除非空文件夹 [用windows自带的cmd提示我“rm”不是内部命令 ...

  7. c# 上传图片流,php端(laravel框架)接收处理方法

    c# httppost方法 public struct PostFile { public string name; public string filename; public Stream bit ...

  8. Voltage Translation for Analog to Digital Interface ADC

    Voltage Translation for Analog to Digital Interface 孕龙逻辑分析仪 ZeroPlus Logic Analyzer How to modify an ...

  9. Delphi 开发ActiveX控件(非ActiveForm)

    Delphi 开发ActiveX控件(非ActiveForm) Q:为什么不采用ActiveForm工程?通过它可以快速开发带窗体控件,创建过程也非常简单(都不用考虑安全接口问题),很省事! A:如果 ...

  10. iOS 转换异步block为同步方式运行

    使用dispatch_semaphore_t 实现 dispatch_semaphore_t sema = dispatch_semaphore_create(0); //创建信号量 __block ...