【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

 

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