说明
- RabbitMQ-Java-06-延迟队列
- 本案例是一个Maven+SpringBoot项目
- 假设你已经实现了上一节死信队列
- 官方文档已包含绝大多数本案例内容。请移步:https://docs.spring.io/spring-amqp/docs/current/reference/html/
核心概念
》延迟队列说明
- 实现方式一:
- 基于直接交换机的普通队列,给队列设置TTL
- 该方式缺点:
- 每个队列的TTL都是固定的,要想不同的TTL只能设置多个TTL,明显不符合我们的需求
- 实现方式二:
- 基于直接交换机的普通队列,消息发送方设置消息的TTL
- 该方式缺点:
- 多个不同TTL的消息发送到同一个队列,队列还是会依次执行消息,导致后边的TTL小的消息比他前边的TTL大的消息还要延后执行,明显不符合我们的需求
- 实现方式三(重点掌握):
- 基于插件:{rabbitmq_delayed_message_exchange},声明交换机的时候指定为{x-delayed-message}类型的交换机
- 发消息的时候设置好消息特殊消息头:{x-delay}
- 文档及下载地址:https://blog.rabbitmq.com/posts/2015/04/scheduling-messages-with-rabbitmq
- 安装
- 将下载的插件拷贝至RabbitMq插件目录(默认:/usr/lib/rabbitmq/plugins),如果没有就新建之
- 开启插件:
[admin@host001 plugins]$ pwd
/usr/lib/rabbitmq/plugins
# 重命名
[admin@host001 plugins]$ sudo mv rabbitmq_delayed_message_exchange-3.9.0.ez rabbitmq_delayed_message_exchange.ez
[admin@host001 plugins]$ ll
total 36
-rw-r--r--. 1 root root 36358 Dec 26 08:52 rabbitmq_delayed_message_exchange.ez
# 开启
[admin@host001 plugins]$ sudo rabbitmq-plugins enable rabbitmq_delayed_message_exchange
Enabling plugins on node rabbit@host001:
rabbitmq_delayed_message_exchange
The following plugins have been configured:
rabbitmq_delayed_message_exchange
rabbitmq_management
rabbitmq_management_agent
rabbitmq_web_dispatch
Applying plugin configuration to rabbit@host001...
The following plugins have been enabled:
rabbitmq_delayed_message_exchange
started 1 plugins.
# 重启RabbitMq
[admin@host001 plugins]$ sudo service rabbitmq-server restart
Redirecting to /bin/systemctl restart rabbitmq-server.service
# 重新RabbitMq登录后台,新建交换机Type选项多了:x-delayed-message,表示插件开启成功
- 该方式的优点:
- 同一个队列可以接收任意delay的消息,处理消息的时候会根据消息的delay时间自动处理排序,nice~
》延迟队列实现过程概览
- idea新建SpringBoot类型module
- 添加Maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.76</version>
</dependency>
- 在默认配置文件中添加RabbitMq配置项
spring.rabbitmq.host=192.168.3.202
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=123456
- 开始搞事情
操作步骤
》实现方式一
- 延迟队列自动配置类:DelayQueueConfig
package cn.cnyasin.rabbit.config;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
/**
* TTL队列自动配置类
*/
@Configuration
public class DelayQueueConfig {
// 定义交换机名
public static final String EXCHANGE_NORMAL = "exchange_normal";
public static final String EXCHANGE_DEAD = "exchange_dead";
// 定义队列名
public static final String QUEUE_NORMAL_1 = "queue_normal_1";
public static final String QUEUE_NORMAL_2 = "queue_normal_2";
public static final String QUEUE_DEAD = "queue_dead";
// 定义路由key
public static final String ROUTING_NORMAL_1 = "routing_normal_1";
public static final String ROUTING_NORMAL_2 = "routing_normal_2";
public static final String ROUTING_DEAD = "routing_dead";
// 声明交换机
@Bean
public DirectExchange ExchangeNormal() {
return new DirectExchange(EXCHANGE_NORMAL);
}
@Bean
public DirectExchange ExchangeDead() {
return new DirectExchange(EXCHANGE_DEAD);
}
// 声明队列
@Bean
public Queue QueueNormal1() {
Map<String, Object> arguments = new HashMap<>();
arguments.put("x-dead-letter-exchange", EXCHANGE_DEAD);
arguments.put("x-dead-letter-routing-key", ROUTING_DEAD);
arguments.put("x-message-ttl", 10000);
return new Queue(QUEUE_NORMAL_1, true, false, false, arguments);
}
@Bean
public Queue QueueNormal2() {
Map<String, Object> arguments = new HashMap<>();
arguments.put("x-dead-letter-exchange", EXCHANGE_DEAD);
arguments.put("x-dead-letter-routing-key", ROUTING_DEAD);
arguments.put("x-message-ttl", 30000);
return new Queue(QUEUE_NORMAL_2, true, false, false, arguments);
}
@Bean
public Queue QueueDead() {
return new Queue(QUEUE_DEAD, true, false, false, null);
}
// 绑定队列、交换机、路由key
@Bean
public Binding QueueNormal1BindingExchangeNormal(
@Qualifier("QueueNormal1") Queue queueNormal1,
@Qualifier("ExchangeNormal") Exchange exchangeNormal
) {
return BindingBuilder.bind(queueNormal1).to(exchangeNormal).with(ROUTING_NORMAL_1).noargs();
}
@Bean
public Binding QueueNormal2BindingExchangeNormal(
@Qualifier("QueueNormal2") Queue queueNormal2,
@Qualifier("ExchangeNormal") Exchange exchangeNormal
) {
return BindingBuilder.bind(queueNormal2).to(exchangeNormal).with(ROUTING_NORMAL_2).noargs();
}
@Bean
public Binding QueueDeadBindingExchangeDead(
@Qualifier("QueueDead") Queue queueDead,
@Qualifier("ExchangeDead") Exchange exchangeDead
) {
return BindingBuilder.bind(queueDead).to(exchangeDead).with(ROUTING_DEAD).noargs();
}
}
- 延迟队列消费者组件:DelayQueueConsumer
package cn.cnyasin.rabbit.consumer;
import cn.cnyasin.rabbit.config.DelayQueueConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.util.Date;
/**
* 延迟队列消费者组件
*/
@Slf4j
@Component
public class DelayQueueConsumer {
/**
* 队列TTL-消费者
*
* @param data
*/
@RabbitListener(queues = DelayQueueConfig.QUEUE_DEAD)
public void ddlQueueConsumer(String data) {
log.info("[*] [{}] 死信队列接收到消息:{}", new Date().toString(), data);
}
}
- 延迟队列控制器:DelayQueueController
package cn.cnyasin.rabbit.controller;
import cn.cnyasin.rabbit.config.DelayQueueConfig;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 延迟队列控制器
*/
@Slf4j
@RestController
@RequestMapping("/delay/queue")
public class DelayQueueController {
@Autowired
RabbitTemplate rabbitTemplate;
/**
* 队列TTL-生产者
*
* @param msg
* @return
*/
@RequestMapping("/ttl/queue/producer/{msg}")
public String TtlQueueProducer(@PathVariable String msg) {
log.info("[*] 准备发送消息:{}", msg);
rabbitTemplate.convertAndSend(DelayQueueConfig.EXCHANGE_NORMAL, DelayQueueConfig.ROUTING_NORMAL_1, msg);
rabbitTemplate.convertAndSend(DelayQueueConfig.EXCHANGE_NORMAL, DelayQueueConfig.ROUTING_NORMAL_2, msg);
return JSON.toJSONString("消息发送成功");
}
}
》实现方式二
- 延迟队列自动配置类:DelayQueueConfig(代码基于实现方式一,以下是新增的部分)
// 定义队列名
public static final String QUEUE_NORMAL_3 = "queue_normal_3";
// 定义路由key
public static final String ROUTING_NORMAL_3 = "routing_normal_3";
@Bean
public Queue QueueNormal3() {
Map<String, Object> arguments = new HashMap<>();
arguments.put("x-dead-letter-exchange", EXCHANGE_DEAD);
arguments.put("x-dead-letter-routing-key", ROUTING_DEAD);
return new Queue(QUEUE_NORMAL_3, true, false, false, arguments);
}
@Bean
public Binding QueueNormal3BindingExchangeNormal(
@Qualifier("QueueNormal3") Queue queueNormal3,
@Qualifier("ExchangeNormal") Exchange exchangeNormal
) {
return BindingBuilder.bind(queueNormal3).to(exchangeNormal).with(ROUTING_NORMAL_3).noargs();
}
- 延迟队列控制器:DelayQueueController(代码基于实现方式一,以下是新增的部分)
/**
* 消息TTL-生产者
*
* @param msg
* @return
*/
@RequestMapping("/ttl/message/producer/{msg}/{ttl}")
public String TtlMessageProducer(@PathVariable String msg, @PathVariable int ttl) throws Exception {
log.info("[*] [{}]准备发送消息:{}", new Date().toString(), msg);
Message message = MessageBuilder.withBody(msg.getBytes("UTF-8"))
.setExpiration(String.valueOf(ttl))
.build();
rabbitTemplate.convertAndSend(DelayQueueConfig.EXCHANGE_NORMAL, DelayQueueConfig.ROUTING_NORMAL_3, message);
return JSON.toJSONString("消息发送成功");
}
》实现方式三
- 延迟队列自动配置类:DelayQueueConfig(代码基于实现方式一,以下是新增的部分)
// 定义交换机名
public static final String EXCHANGE_DELAY = "exchange_delay";
// 定义队列名
public static final String QUEUE_DELAY = "queue_delay";
// 定义路由key
public static final String ROUTING_DELAY = "routing_delay";
// 声明交换机
@Bean
public CustomExchange ExchangeDelay() {
Map<String, Object> arguments = new HashMap<>();
arguments.put("x-delayed-type", "direct");
return new CustomExchange(EXCHANGE_DELAY, "x-delayed-message", true, false, arguments);
}
// 声明队列
@Bean
public Queue QueueDelay() {
return new Queue(QUEUE_DELAY, true, false, false, null);
}
// 绑定队列、交换机、路由key
@Bean
public Binding QueueDelayBindingExchangeDelay(
@Qualifier("QueueDelay") Queue queueDelay,
@Qualifier("ExchangeDelay") Exchange exchangeDelay
) {
return BindingBuilder.bind(queueDelay).to(exchangeDelay).with(ROUTING_DELAY).noargs();
}
- 延迟队列消费者组件:DelayQueueConsumer(代码基于实现方式一,以下是新增的部分)
/**
* delay队列-消费者
*
* @param data
*/
@RabbitListener(queues = DelayQueueConfig.QUEUE_DELAY)
public void delayQueueConsumer(String data) {
log.info("[*] [{}] 延迟队列接收到消息:{}", new Date().toString(), data);
}
- 延迟队列控制器:DelayQueueController(代码基于实现方式一,以下是新增的部分)
/**
* delay消息-生产者
*
* @param msg
* @return
*/
@RequestMapping("/delay/message/producer/{msg}/{ttl}")
public String DelayMessageProducer(@PathVariable String msg, @PathVariable int ttl) throws Exception {
log.info("[*] [{}]准备发送消息:{}", new Date().toString(), msg);
Message message = MessageBuilder.withBody(msg.getBytes("UTF-8"))
.setHeader("x-delay", String.valueOf(ttl))
.build();
rabbitTemplate.convertAndSend(DelayQueueConfig.EXCHANGE_DELAY, DelayQueueConfig.ROUTING_DELAY, message);
return JSON.toJSONString("消息发送成功");
}
备注
- 该教程部分内容收集自网络,感谢原作者。
附录
- 无