前言
基于死信的延时队列文章链接
在上一篇rabbitmq基于死信的延时队列中提到了,通过rabbitmq的消息过期机制实现的延时队列,RabbitMQ 只会检查第一个消息是否过期,如果过期则丢到死信队列,如果第一个消息的延时时长很长,而第二个消息的延时时长很短,第二个消息并不会优先得到执行。
rabbitmq之基于插件的延时队列
针对这一问题我们就可以通过rabbitmq_delayed_message_exchange插件实现rabbitmq的延时队列。从而达到并行计算消息过期时间的问题。
安装延时队列插件
- 通过官网下载 https://www.rabbitmq.com/community-plugins.html,
rabbitmq_delayed_message_exchange 插件,然后解压放置到 RabbitMQ 的插件目录。 - 接下来,进入RabbitMQ的安装目录下的sbin目录,执行下面命令让该插件生效,然后重启RabbitMQ。
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
安装成功后,rabbitmq监控页面的交换机会新增一个
x-delayed-message的类型。
流程图
基于死信的延时队列流程图
基于rabbitmq_delayed_message_exchange插件的流程图
代码演示
代码模拟下图
配置文件中添加rabbitmq的配置
spring.rabbitmq.host=192.168.136.128
spring.rabbitmq.port=5672
spring.rabbitmq.username=admin
spring.rabbitmq.password=admin
引入rabbitmq的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
添加配类
@Configuration
public class DelayedQueueConfig {
//延迟队列名称
public static final String DELAYED_QUEUE_NAME = "delayed.queue";
//延迟交换机名称
public static final String DELAYED_EXCHANGE_NAME = "delayed.exchange";
//延迟队列routingkey
public static final String DELAYED_ROUTING_KEY = "delayed.routingkey";
//声明延迟队列
@Bean("delayedQueue")
public Queue delayedQueue() {
return new Queue(DELAYED_QUEUE_NAME);
}
//自定义交换机 我们在这里定义的是一个延迟交换机
@Bean("delayedExchange")
public CustomExchange delayedExchange()
{
Map<String, Object> args = new HashMap<>();
//自定义延迟交换机的类型,fanout丶direct丶Topic都可以根据业务来
args.put("x-delayed-type", "direct");
return new CustomExchange(DELAYED_EXCHANGE_NAME, "x-delayed-message", true, false,args);
}
//将延迟队列与延迟交换机绑定
@Bean
public Binding bindingDelayedQueue(@Qualifier("delayedQueue") Queue queue,
@Qualifier("delayedExchange") CustomExchange delayedExchange) {
return BindingBuilder.bind(queue).to(delayedExchange).with(DELAYED_ROUTING_KEY).noargs();
}
}
//因为是基于插件的延时队列,所以声明交换机的时候,类型需要是定制的CustomExchange
添加生产者
@Slf4j
@RequestMapping
@RestController
public class DelayedQueueProducer{
@Autowired
private RabbitTemplate rabbitTemplate;
@GetMapping("sendDelayMsg/{message}/{delayTime}")
public void sendMsg((@PathVariable String message,@PathVariable Integer delayTime){
log.info("当前时间:{},发送一条时长{}毫秒消息给延迟队列delayed.queue:{},new Date(),toString(),delayTime,message");
rabbitTemplate.convertAndSend(DelayedQueueConfig.DELAYED_EXCHANGE_NAME, DelayedQueueConfig.DELAYED_ROUTING_KEY, message,msg->{
msg.getMessageProperties().setDelay(delayTime);
return msg;
});
}
}
添加消费者
@Slf4j
@Component
public class DelayedQueueConsumer{
//指定消费的队列
@RabbitListener(queues = DelayedQueueConfig.DELAYED_QUEUE_NAME)
public void receiveDelayedQueue(Message message)
{
String msg = new String(message.getBody());
log.info("当前时间:{},收到延迟队列的消息:{}", new Date().toString(), msg);
}
}
发送请求,模拟发送一条延时20s的消息和延时2s的消息
首先发送 sendDelayMsg/come on baby1/20000
接着发送 sendDelayMsg/come on baby2/2000
观察结果
我们发现虽然发送的第一条消息是baby1,但是这次baby2先被消费掉了,所以证明通过插件实现的延时队列,可以并行计算过期时间。