【RabbitMq 篇四】-延迟队列(DLX+TTL)

前言

延迟消费在 RabbitMq 没有属性可以设置,只能通过 死信交换器(DLX)和设置过期时间(TTL)结合起来达到延迟的效果,所以我要介绍DLX和TTL以及实现延迟队列。

 

 

正文

 

使用所有框架和中间件的版本

环境
框架 版本
Spring Boot                 2.1.5.RELEASE
RabbitMq 3.7.15
JDK

1.8.0_144

Erlang 22.0.2

 

 

过期时间(TTL)

 

TTL是Time To Live的缩写, 也就是生存时间。RabbitMq支持对消息和队列设置TTL,对消息这设置是在发送的时候指定,对队列设置是从消息入队列开始计算, 只要超过了队列的超时时间配置, 那么消息会自动清除。

如果两种方式一起使用消息对TTL和队列的TTL之间较小的为准,也就是消息5s过期,队列是10s,那么5s的生效。

默认是没有过期时间的,表示消息没有过期时间;如果设置为0,表示消息在投递到消费者的时候直接被消息,否则丢弃。

设置消息的过期时间用  x-message-ttl 参数实现,单位毫秒。

设置队列的过期时间用 x-expires 参数,单位毫秒,注意,不能设置为0。

 

死信交换器(DLX)

 

DLX是Dead-Letter-Exchange的缩写,全称死信交换器。成为死信队列后,可以被重新发送到另外一个交换器中,这个交换器就是DLX,绑定DLX到队列称为死信队列。注意,死信队列和死信交换器不一样哦

成为死信一般由以下几种情况:

  • 消息被拒绝 (basic.reject or basic.nack) 且带 requeue=false 参数
  • 消息的TTL-存活时间已经过期
  • 队列长度限制被超越(队列满)

 

DLX和一般的交换器没有区别,可以声明在任何的队列上,理解为队列的一个属性吧。当这个队列有死信时会根据设置自动的将死信重新发布到设置的DLX上进行消费。这个消费了死信的队列称之为死信队列,并不是绑定了DLX的队列是死信队列,大家要区分清楚。

通过在队列里设置  x-dead-letter-exchange 参数来声明DLX,如果当前DLX是 direct 类型还要 声明 x-dead-letter-routing-key  参数来指定路由键,如果没有指定,则使用原队列的路由键。

 

延迟队列

 

通过DLX和TTL模拟出延迟队列的功能,即,消息发送以后,不让消费者拿到,而是等待过期时间,变成死信后,发送给死信队列进行消费。

 

延迟队列流程图

 

maven依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

 

 

RabbitMq配置

spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=yanlin
spring.rabbitmq.password=yanlin

 

生产者

 

@RestController
public class DelayController {

    @Autowired
    private AmqpTemplate amqpTemplate;

    @GetMapping("/delay/{id}")
    public String delayTest(@PathVariable Integer id) {
        Order order = new Order(id, "我等了10s");
        amqpTemplate.convertAndSend("normal_exchange", "normal_key", order);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
        String dateString = sdf.format(new Date());
        return "请求时间" + dateString;
    }
}

 

 

配置监听

 

@Configuration
public class DelayListener {
    //死信队列
    private static final String DELAY_QUEUE = "delay_queue";
    //正常队列
    private static final String NORMAL_QUEUE = "normal_queue";
    //死信交换器
    private static final String DELAY_EXCHANGE = "delay_exchange";
    //死信路由键
    private static final String DELAY_KEY = "delay_key";

    /**
     * 正常队列
     *
     * @return
     */
    @Bean
    public Queue normalQueue() {
        Map<String, Object> map = new HashMap<>();
        map.put("x-message-ttl", 10000);//设置10s过期时间
        //x-dead-letter-exchange参数是设置该队列的死信交换器(DLX)
        map.put("x-dead-letter-exchange", DELAY_EXCHANGE);
        //x-dead-letter-routing-key参数是给这个DLX指定路由键
        map.put("x-dead-letter-routing-key", DELAY_KEY);
        return new Queue(NORMAL_QUEUE, true, false, false, map);
    }

    /**
     * 正常交换器
     *
     * @return
     */
    @Bean
    public DirectExchange normalExchange() {
        return new DirectExchange("normal_exchange", true, false);
    }

    /**
     * 正常队列绑定
     *
     * @param normalQueue
     * @param normalExchange
     * @return
     */
    @Bean
    public Binding normalBinding(Queue normalQueue, DirectExchange normalExchange) {
        return BindingBuilder.bind(normalQueue).to(normalExchange).with("normal_key");
    }

    /**
     * 死信队列
     *
     * @return
     */
    @Bean
    public Queue delayQueue() {
        return new Queue(DELAY_QUEUE);
    }

    /**
     * 死信交换器
     *
     * @return
     */
    @Bean
    public DirectExchange delayExchange() {
        return new DirectExchange(DELAY_EXCHANGE, true, false);
    }


    /**
     * 死信队列绑定交换器
     *
     * @param delayQueue
     * @param delayExchange
     * @return
     */
    @Bean
    Binding delayBinding(Queue delayQueue, DirectExchange delayExchange) {
        return BindingBuilder.bind(delayQueue).to(delayExchange).with(DELAY_KEY);
    }

 

 

消费者

@Service
public class DelayConsumer {

    /**
     * 延迟消费方法
     *
     * @param order
     */
    @RabbitListener(queues = "delay_queue")
    @RabbitHandler
    public void delay(Order order) {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
        System.out.println("------" + sdf.format(new Date()) + "------");
        System.out.println("请观察页面返回的时间与上面打印时间对比");

        System.out.println("这是延迟10s消费的消息:" + order.getName());
    }

}

 

启动项目,生产者发送消息后返回的时间与消费者控制台打印的时间对比,差了10s,延迟队列完成。

 

 

 

项目如图

github地址 https://github.com/362460453/rabbitMQ-demo

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章