需求描述:在 Spring Boot 项目中用 MQ 处理耗时的下载任务,过程中需要停止所有 worker,等将临时文件清理后,继续后续下载任务。
1. RabbitMQ 环境
使用 docker 准备环境
version: '3'
services:
rabbitmq:
image: "rabbitmq:management"
container_name: "rabbitmq-basic"
ports:
- "5672:5672"
- "15672:15672"
environment:
- TZ="Asia/Shanghai"
- RABBITMQ_DEFAULT_USER=${RABBITMQ_DEFAULT_USER}
- RABBITMQ_DEFAULT_PASS=${RABBITMQ_DEFAULT_PASS}
volumes:
- /etc/localtime:/etc/localtime:ro
- ./data/rabbitmq_data:/var/lib/rabbitmq
entrypoint: rabbitmq-server
2. 添加 Maven 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
3. 修改配置文件
application.yaml
spring:
rabbitmq:
host: "localhost"
port: 5672
username: "root"
password: "123456"
listener:
simple:
prefetch: 4
concurrency: 4
concurrency
指定并发数量为 4,prefetch
为消息预取的数量,如果将prefetch
设置为 1,并发的数量仍未 1。
4. 添加 RabbitMQ 配置类
本例中声明一个名为 myqueue
的队列,RabbitAdmin
用于后续获取队列的状态
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MQConfig {
@Autowired
private ConnectionFactory connectionFactory;
@Bean
public RabbitAdmin rabbitAdmin() {
return new RabbitAdmin(connectionFactory);
}
@Bean
public Queue myQueue() {
return new Queue("myqueue");
}
}
5. 创建消费者
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
public class Listener {
@RabbitListener(queues = "myqueue", id = "myqueue-listener")
public void processMessage(String message) throws InterruptedException {
System.out.println("Received: " + message);
// 模拟耗时任务
Thread.sleep(10000);
}
}
6. Web Controller 中添加测试方法
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Properties;
@RestController
public class MQController {
private final RabbitTemplate rabbitTemplate;
private final RabbitAdmin rabbitAdmin;
private final RabbitListenerEndpointRegistry registry;
public MQController(RabbitTemplate rabbitTemplate, RabbitAdmin rabbitAdmin, RabbitListenerEndpointRegistry registry) {
this.rabbitTemplate = rabbitTemplate;
this.rabbitAdmin = rabbitAdmin;
this.registry = registry;
}
@GetMapping("/send")
public String send() {
for (int i = 0; i < 100; i++) {
rabbitTemplate.convertAndSend("myqueue", "Hello, RabbitMQ! " + i);
}
return "Message sent!";
}
@GetMapping("/receive")
public String receive() {
String message = (String) rabbitTemplate.receiveAndConvert("myqueue");
return message != null ? "Received message: " + message : "No message!";
}
@GetMapping("/stop")
public String stop() {
registry.getListenerContainer("myqueue-listener").stop(() ->
System.out.println("Listener stopped! and do something else..."));
return "Listener stopped!";
}
@GetMapping("/start")
public String start() {
registry.getListenerContainer("myqueue-listener").start();
return "Listener start!";
}
@GetMapping("/queue_status")
public String getQueueStatus() {
Properties queueInformation = rabbitAdmin.getQueueProperties("myqueue");
if (queueInformation != null) {
return "Queue Status: " + queueInformation;
} else {
return "Queue not found";
}
}
}
接口解释:
/send
: 批量发送消息
/receive
: 手动处理单条消息
/stop
: Listener 停止工作 ,匿名函数中传入所有 worker 停止之后需要进行的操作
/start
: Listener 开始工作
/queue_status
: 获取当前队列状态,返回示例:
Queue Status: {QUEUE_NAME=myqueue, QUEUE_MESSAGE_COUNT=58, QUEUE_CONSUMER_COUNT=4}