Springboot2.x集成Rabbitmq实现延时消费

前言

比如我们在使用mq的时候并不希望立即收到消息,比如实现3分钟后再查询订单,这时候我们就需要使用延迟消息发送了。

RabbitMQ如何实现迟队列?

AMQP协议和RabbitMQ队列本身没有直接支持延迟队列功能。但是我们可以通过RabbitMQ的两个特性来曲线实现延迟队列:

特性1、Time To Live(TTL)

1.RabbitMQ可以针对Queue设置x-expires

2.针对Message设置 x-message-ttl

通过这两种方式来控制消息的生存时间

  • 如果通过队列属性设置,队列中所有消息都有相同的过期时间。
  • 如果通过对消息进行单独设置,每条消息TTL可以不同。
  • 如果超时(两者同时设置以最先到期的时间为准),则消息变为dead letter(死信)

特性2、Dead Letter Exchanges(DLX)

RabbitMQ的Queue可以配置x-dead-letter-exchange 和x-dead-letter-routing-key(可选)两个参数,如果队列内出现了dead letter,则按照这两个参数重新路由转发到指定的队列。

  • x-dead-letter-exchange:出现dead letter之后将dead letter重新发送到指定exchange
  • x-dead-letter-routing-key:出现dead letter之后将dead letter重新按照指定的routing-key发送

队列出现dead letter的情况有:

  • 消息或者队列的TTL过期
  • 队列达到最大长度
  • 消息被消费端拒绝(basic.reject or basic.nack)并且requeue=false

综合上述两个特性,设置了TTL规则之后当消息在一个队列中变成死信时,利用DLX特性它能被重新转发到另一个Exchange或者Routing Key,这时候消息就可以重新被消费了。

方式一:

    /死信队列的应用//

    /**
     * DLX测试队列
     */
    @Bean
    public Queue DLXTestQueue() {
        Map<String, Object> args = new HashMap<>(2);
        args.put("x-dead-letter-exchange", "DL_exchange");
        args.put("x-dead-letter-routing-key", "DL_queue");
        return QueueBuilder.nonDurable("DLX-test-queue").withArguments(args).build();
    }

    @Bean
    public Exchange DLXTestExchange() {
        return ExchangeBuilder.directExchange("DLX-test-exchange").durable(true).build();
    }

    @Bean
    public Binding DLXTestBinding() {
        return new Binding("DLX-test-queue", Binding.DestinationType.QUEUE, "DLX-test-exchange", "DLX-test-queue", null);
    }


    ///声明一个死信队列,交换机,绑定//
    @Bean
    public Queue dlQueue() {
        return QueueBuilder.nonDurable("DL_queue").build();
    }

    @Bean
    public Exchange dlExchange() {
        return ExchangeBuilder.directExchange("DL_exchange").durable(true).build();
    }

    /**
     * 死信路由通过 DL_queue 绑定键绑定到死信队列上.
     */
    @Bean
    public Binding deadLetterBinding() {
        return new Binding("DL_queue", Binding.DestinationType.QUEUE, "DL_exchange", "DL_queue", null);
    }

发送:

    /**
     * 测试死信队列
     */
    @GetMapping("ttl")
    public Resp testTTL() {
        rabbitTemplate.convertAndSend("DLX-test-exchange", "DLX-test-queue", "去私信队列", messagePostProcessor -> {
            messagePostProcessor.getMessageProperties().setExpiration("10000");
            return messagePostProcessor;
        });
        return Resp.success("ok", null);
    }

 消费者:

    @RabbitListener(queues = {"DL_queue"})
    public void DLXTestQueue(Message message, Channel channel) throws IOException {
        log.info("死信队列  10s 后 消费消息 {}", new String(message.getBody()));
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }

方式二:在rabbitmq 3.5.7及以上的版本提供了一个插件(rabbitmq-delayed-message-exchange)来实现延迟队列功能。同时插件依赖Erlang/OPT 18.0及以上。

插件下载地址:https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases,具体可参考我上一篇文章

配置:

package com.zoo.mq.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;


/**
 * @author: 谢飞
 */
@Configuration
public class RabbitMqConfig {

    /**
     * 定义延迟消息发送的队列.
     */
    @Bean
    public Queue delayQueue() {
        return QueueBuilder.nonDurable("delay_queue").build();
    }


    /交换机/

    /**
     * 定义一个用于延迟消息发送的交换机
     * 延时队列交换机 注意这里的交换机类型:CustomExchange
     */
    @Bean
    public CustomExchange delayExchange() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-delayed-type", "direct");
        return new CustomExchange("delay_exchange", "x-delayed-message", true, false, args);
    }


    绑定/

    /**
     * 给延时队列绑定交换机
     */
    @Bean
    public Binding redirectBinding() {
        return BindingBuilder.bind(delayQueue()).to(delayExchange()).with("delay_key").noargs();
    }

}

生产者:

    @GetMapping("sendTTL")
    public Resp sendTTL() {
        rabbitTemplate.convertAndSend("delay_exchange", "delay_key", "hello", messagePostProcessor -> {
            messagePostProcessor.getMessageProperties().setDelay(10000);
            return messagePostProcessor;
        });
        return Resp.success("ok", null);
    }

 消费者:

    @RabbitListener(queues = {"delay_queue"})
    public void delayQueue(Message message, Channel channel) throws IOException {
        log.info("delay_queue  10s 后 消费消息 {}", new String(message.getBody()));
    }

测试ok。 

使用rabbitmq-delayed-message-exchange插件时发送到队列的消息数量在web管理界面可能不可见,不影响正常功能使用

猜你喜欢

转载自blog.csdn.net/qq_36850813/article/details/103279801