RabbitMQ 入门到应用 ( 七 ) 延迟队列

8.延时队列

8.1.基本概念

8.1.1.消息的TTL(Time To Live)

消息的TTL就是消息的存活时间。

RabbitMQ可以对队列和消息分别设置TTL。对队列设置就是队列中没有被消费的消息的保留时间,也可以对每一个单独的消息做单独的设置。

超过了这个时间,我们认为这个消息就死了,称之为死信

如果队列设置了,消息也设置了,那么会取小的。

8.1.2.Dead Letter Exchanges(DLX)

一个消息在满足如下条件下,就成为死信, 会进入死信交换机,记住这里是交换机而不是队列,一个交换机可以对应很多队列。

  1. 一个消息被Consumer消费者拒收了,并且reject方法的参数里requeue是false。也就是说不会被再次放在队列里,被其他消费者使用。

  2. 上面的消息的TTL到了,消息过期了。

  3. 队列的长度限制满了。排在前面的消息会被丢弃或者扔到死信路由上。

Dead Letter Exchange死信交换机其实就是一种普通的exchange交换机,和创建其他exchange交换面没有两样。只是在某一个设置Dead Letter Exchange的队列中有消息过期了,会自动触发消息的转发,发送到Dead Letter Exchange中去。

8.1.3.队列设置死信属性

x-dead-letter-exchange   指向死信交换机
x-dead-letter-routing-key  发送死信时的路由键
x-message-ttl   消息过期时间, 毫秒数

8.2.实例一 : 过期死信

RabbitMQ 入门到应用 ( 七 ) 延迟队列_第1张图片

8.2.1.创建相关对象

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

@Configuration
public class RabbitTimeoutConfig {

    @Bean
    public Queue timeoutDelayQueue(){
        Map<String, Object> arguments = new HashMap<>(3);
        // 设置死信交换机
        arguments.put("x-dead-letter-exchange", "delay-exchange" );
        // 设置死信路由键
        arguments.put("x-dead-letter-routing-key", "delay.timeout");
        // 设置过期时间
        arguments.put("x-message-ttl", 60000);
        // 队列名称 , 是否为持久性 , 是否为独享 , 是否自动删除, 属性设置
        return new Queue("timeout.delay.queue", true, false, false, arguments);
    }

    @Bean
    public Queue baseQueue(){
        // 队列名称 , 是否为持久性 , 是否为独享 , 是否自动删除
        return new Queue("base.queue", true, false, false, null);
    }



    @Bean
    public Exchange timeoutExchange(){
        // 交换机名称 ,  是否为持久性  , 是否自动删除, 属性
        return new TopicExchange("timeout-exchange", true, false, null);
    }

    @Bean
    public Exchange delayExchange(){
        // 交换机名称 ,  是否为持久性  , 是否自动删除, 属性
        return new TopicExchange("delay-exchange", true, false, null);
    }


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


    @Bean
    public Binding baseBindingDelay(){
        return new Binding("base.queue", Binding.DestinationType.QUEUE, "delay-exchange" , "delay.timeout", null);
    }

}

8.2.2.发送消息

    @RequestMapping("/sender/test3")
    public String test3(){
        String msg = "Mode delay, Rabbit MQ . " + new Date();
        String cd = UUID.randomUUID().toString();
        CorrelationData correlationData = new CorrelationData(cd);
        //   交换机, 路由键, 消息, 消息id
        rabbitTemplate.convertAndSend("timeout-exchange","test.timeout", msg , correlationData);
        return msg;
    }

8.2.3.监听消息

import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;

import java.util.Date;


@Service
public class TimeoutService {

    @RabbitListener(queues = "base.queue")
    @RabbitHandler
    public void timeoutListener(Message message){
        System.out.println("message = " + message);
        System.out.println( " time = " + new Date());
    }
}

8.3.实例 二 : 一个交换机

只使用一个交接机来实现

RabbitMQ 入门到应用 ( 七 ) 延迟队列_第2张图片

8.3.1.创建相关对象

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.TopicExchange;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

@Configuration
public class RabbitTimeoutConfig {

    
    @Bean
    public Queue baseQueue(){
        // 队列名称 , 是否为持久性 , 是否为独享 , 是否自动删除
        return new Queue("base.queue", true, false, false, null);
    }


    @Bean
    public Queue newDelayQueue(){
        Map<String, Object> arguments = new HashMap<>(3);
        // 设置死信交换机
        arguments.put("x-dead-letter-exchange", "timeout-delay-exchange" );
        // 设置死信路由键
        arguments.put("x-dead-letter-routing-key", "new.delay.timeout");
        // 设置过期时间
        arguments.put("x-message-ttl", 60000);
        // 队列名称 , 是否为持久性 , 是否为独享 , 是否自动删除, 属性设置
        return new Queue("new.delay.queue", true, false, false, arguments);
    }



    @Bean
    public Exchange timeoutDelayExchange(){
        // 交换机名称 ,  是否为持久性  , 是否自动删除, 属性
        return new TopicExchange("timeout-delay-exchange", true, false, null);
    }


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


    @Bean
    public Binding baseBindingTimeoutDelay(){
        return new Binding("base.queue", Binding.DestinationType.QUEUE, "timeout-delay-exchange" , "new.delay.timeout", null);
    }

}

8.3.2.发送消息

    @RequestMapping("/sender/test4")
    public String test4(){
        String msg = "Mode delay 2, Rabbit MQ . " + new Date();
        String cd = UUID.randomUUID().toString();
        CorrelationData correlationData = new CorrelationData(cd);
        //   交换机, 路由键, 消息, 消息id
        rabbitTemplate.convertAndSend("timeout-delay-exchange","new.test.timeout", msg , correlationData);
        return msg;
    }

8.3.3.监听消息

与 实例一 相同

8.4.实例三 : 拒收消息

8.4.1.取消过期时间

修改 实例二 中, 死信队列 , 注释掉 过期赶时间

  @Bean
    public Queue newDelayQueue(){
        Map<String, Object> arguments = new HashMap<>(3);
        // 设置死信交换机
        arguments.put("x-dead-letter-exchange", "timeout-delay-exchange" );
        // 设置死信路由键
        arguments.put("x-dead-letter-routing-key", "new.delay.timeout");
        // 设置过期时间
        // arguments.put("x-message-ttl", 60000);
        // 队列名称 , 是否为持久性 , 是否为独享 , 是否自动删除, 属性设置
        return new Queue("new.delay.queue", true, false, false, arguments);
    }

8.4.2.增加死信队列监听

    @RabbitListener(queues = "new.delay.queue")
    @RabbitHandler
    public void newTimeoutListener(Message message, Channel channel) throws IOException {
        System.out.println("message = " + message);
        System.out.println( " time = " + new Date());

        long deliveryTag = message.getMessageProperties().getDeliveryTag();
        // 拒收消息, 并 不会将信息 放回到队列
        channel.basicNack(deliveryTag, false, false);

    }

8.4.3.测试

发消息测试, 立刻收到消息

8.5.实例四 : 设置消息过期时间

设置 消息的过期时间, 实现死信

8.5.1.取消对死信的监听

注释掉 对死信监听的代码

8.5.2.发送消息时设置

    @RequestMapping("/sender/test5")
    public String test5(){
        String msg = "Mode 设置消息过期时间, Rabbit MQ . " + new Date();
        String cd = UUID.randomUUID().toString();
        CorrelationData correlationData = new CorrelationData(cd);

        rabbitTemplate.convertAndSend("timeout-delay-exchange","new.test.timeout", msg, (messagePostProcessor) -> {
            // 消息设置成 60 秒过期
            messagePostProcessor.getMessageProperties().setExpiration("60000");
            return messagePostProcessor;
        }, correlationData);
        return msg;
    }

8.5.3.测试

测试 1 分钟后, 会收到消息, 这样过期时间就由 消息来 设置

但 有这样的一个问题

多个不同过期时间消息进入队列, 队列会根据第一个接收到的消息的过期时间来等待

8.5.3.1.增加新的请求

增加新的请求, 分别设置过期时间 为 40 秒, 20 秒


    @RequestMapping("/sender/test6")
    public String test6(){
        String msg = "Mode 设置消息过期时间, Rabbit MQ . " + new Date();
        String cd = UUID.randomUUID().toString();
        CorrelationData correlationData = new CorrelationData(cd);

        rabbitTemplate.convertAndSend("timeout-delay-exchange","new.test.timeout", msg, (messagePostProcessor) -> {
            // 消息设置成 40 秒过期
            messagePostProcessor.getMessageProperties().setExpiration("40000");
            return messagePostProcessor;
        }, correlationData);
        return msg;
    }



    @RequestMapping("/sender/test7")
    public String test7(){
        String msg = "Mode 设置消息过期时间, Rabbit MQ . " + new Date();
        String cd = UUID.randomUUID().toString();
        CorrelationData correlationData = new CorrelationData(cd);

        rabbitTemplate.convertAndSend("timeout-delay-exchange","new.test.timeout", msg, (messagePostProcessor) -> {
            // 消息设置成 20 秒过期
            messagePostProcessor.getMessageProperties().setExpiration("20000");
            return messagePostProcessor;
        }, correlationData);
        return msg;
    }


8.5.3.2.依次请求

依次 发出 test5, test6, test7 请求

分别对应的过期时间是 60 秒, 40秒 , 20秒,

但 要等到 60秒, 才收到死信消息, 是因为 第一个 test5 要等待 60 秒

你可能感兴趣的:(RabbitMQ,微服务,java-rabbitmq,rabbitmq,java)