RabbitMQ學習之延時隊列

在實際的業務中我們會遇見生產者產生的消息,不立即消費,而是延時一段時間在消費。RabbitMQ本身沒有直接支持延遲隊列功能,但是我們可以根據其特性Per-Queue Message TTL和 Dead Letter Exchanges實現延時隊列。也可以通過改特性設置消息的優先級。

1.Per-Queue Message TTL
RabbitMQ可以針對消息和隊列設置TTL(過期時間)。隊列中的消息過期時間(Time To Live, TTL)有兩種方法可以設置。第一種方法是通過隊列屬性設置,隊列中所有消息都有相同的過期時間。第二種方法是對消息進行單獨設置,每條消息TTL可以不同。如果上述兩種方法同時使用,則消息的過期時間以兩者之間TTL較小的那個數值爲準。消息在隊列的生存時間一旦超過設置的TTL值,就成爲dead message,消費者將無法再收到該消息。
2.Dead Letter Exchanges
當消息在一個隊列中變成死信後,它能被重新publish到另一個Exchange。消息變成Dead Letter一向有以下幾種情況:
消息被拒絕(basic.reject or basic.nack)並且requeue=false
消息TTL過期
隊列達到最大長度
實際上就是設置某個隊列的屬性,當這個隊列中有Dead Letter時,RabbitMQ就會自動的將這個消息重新發布到設置的Exchange中去,進而被路由到另一個隊列,publish可以監聽這個隊列中消息做相應的處理,這個特性可以彌補RabbitMQ 3.0.0以前支持的immediate參數中的向publish確認的功能。

一、在隊列上設置TTL


1.建立delay.exchange


這裏Internal設置爲NO,否則將無法接受dead letter,YES表示這個exchange不可以被client用來推送消息,僅用來進行exchange和exchange之間的綁定。

2.建立延時隊列(delay queue)


如上配置延時5min隊列(x-message-ttl=300000)

x-max-length:最大積壓的消息個數,可以根據自己的實際情況設置,超過限制消息不會丟失,會立即轉向delay.exchange進行投遞

x-dead-letter-exchange:設置爲剛剛配置好的delay.exchange,消息過期後會通過delay.exchange進行投遞

這裏不需要配置”dead letter routing key”否則會覆蓋掉消息發送時攜帶的routingkey,導致後面無法路由爲剛纔配置的delay.exchange

3.配置延時路由規則

需要延時的消息到exchange後先路由到指定的延時隊列

1)創建delaysync.exchange通過Routing key將消息路由到延時隊列


2.配置delay.exchange 將消息投遞到正常的消費隊列

配置完成。

下面使用代碼測試一下:

生產者:

  1. package cn.slimsmart.study.rabbitmq.delayqueue.queue;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import com.rabbitmq.client.Channel;  
  6. import com.rabbitmq.client.Connection;  
  7. import com.rabbitmq.client.ConnectionFactory;  
  8.   
  9. public class Producer {  
  10.   
  11.     private static String queue_name = “test.queue”;  
  12.   
  13.     public static void main(String[] args) throws IOException {  
  14.         ConnectionFactory factory = new ConnectionFactory();  
  15.         factory.setHost(”10.1.199.169”);  
  16.         factory.setUsername(”admin”);  
  17.         factory.setPassword(”123456”);  
  18.         Connection connection = factory.newConnection();  
  19.         Channel channel = connection.createChannel();  
  20.         // 聲明隊列  
  21.         channel.queueDeclare(queue_name, truefalsefalsenull);  
  22.         String message = ”hello world!” + System.currentTimeMillis();  
  23.         channel.basicPublish(”delaysync.exchange”“deal.message”null, message.getBytes());  
  24.         System.out.println(”sent message: ” + message + “,date:” + System.currentTimeMillis());  
  25.         // 關閉頻道和連接  
  26.         channel.close();  
  27.         connection.close();  
  28.     }  
  29. }  
package cn.slimsmart.study.rabbitmq.delayqueue.queue;

import java.io.IOException;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class Producer {

    private static String queue_name = "test.queue";

    public static void main(String[] args) throws IOException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("10.1.199.169");
        factory.setUsername("admin");
        factory.setPassword("123456");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        // 聲明隊列
        channel.queueDeclare(queue_name, true, false, false, null);
        String message = "hello world!" + System.currentTimeMillis();
        channel.basicPublish("delaysync.exchange", "deal.message", null, message.getBytes());
        System.out.println("sent message: " + message + ",date:" + System.currentTimeMillis());
        // 關閉頻道和連接
        channel.close();
        connection.close();
    }
}
消費者:

  1. package cn.slimsmart.study.rabbitmq.delayqueue.queue;  
  2.   
  3. import com.rabbitmq.client.Channel;  
  4. import com.rabbitmq.client.Connection;  
  5. import com.rabbitmq.client.ConnectionFactory;  
  6. import com.rabbitmq.client.QueueingConsumer;  
  7.   
  8. public class Consumer {  
  9.   
  10.     private static String queue_name = “test.queue”;  
  11.   
  12.     public static void main(String[] args) throws Exception {  
  13.         ConnectionFactory factory = new ConnectionFactory();  
  14.         factory.setHost(”10.1.199.169”);  
  15.         factory.setUsername(”admin”);  
  16.         factory.setPassword(”123456”);  
  17.         Connection connection = factory.newConnection();  
  18.         Channel channel = connection.createChannel();  
  19.         // 聲明隊列  
  20.         channel.queueDeclare(queue_name, truefalsefalsenull);  
  21.         QueueingConsumer consumer = new QueueingConsumer(channel);  
  22.         // 指定消費隊列  
  23.         channel.basicConsume(queue_name, true, consumer);  
  24.         while (true) {  
  25.             // nextDelivery是一個阻塞方法(內部實現其實是阻塞隊列的take方法)  
  26.             QueueingConsumer.Delivery delivery = consumer.nextDelivery();  
  27.             String message = new String(delivery.getBody());  
  28.             System.out.println(”received message:” + message + “,date:” + System.currentTimeMillis());  
  29.         }  
  30.     }  
  31.   
  32. }  
package cn.slimsmart.study.rabbitmq.delayqueue.queue;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;

public class Consumer {

    private static String queue_name = "test.queue";

    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("10.1.199.169");
        factory.setUsername("admin");
        factory.setPassword("123456");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        // 聲明隊列
        channel.queueDeclare(queue_name, true, false, false, null);
        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 指定消費隊列
        channel.basicConsume(queue_name, true, consumer);
        while (true) {
            // nextDelivery是一個阻塞方法(內部實現其實是阻塞隊列的take方法)
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println("received message:" + message + ",date:" + System.currentTimeMillis());
        }
    }

}
二、在消息上設置TTL


實現代碼:

生產者:

  1. package cn.slimsmart.study.rabbitmq.delayqueue.message;  
  2.   
  3. import java.io.IOException;  
  4. import java.util.HashMap;  
  5.   
  6. import com.rabbitmq.client.AMQP;  
  7. import com.rabbitmq.client.Channel;  
  8. import com.rabbitmq.client.Connection;  
  9. import com.rabbitmq.client.ConnectionFactory;  
  10.   
  11. public class Producer {  
  12.   
  13.     private static String queue_name = “message_ttl_queue”;  
  14.   
  15.     public static void main(String[] args) throws IOException {  
  16.         ConnectionFactory factory = new ConnectionFactory();  
  17.         factory.setHost(”10.1.199.169”);  
  18.         factory.setUsername(”admin”);  
  19.         factory.setPassword(”123456”);  
  20.         Connection connection = factory.newConnection();  
  21.         Channel channel = connection.createChannel();  
  22.         HashMap<String, Object> arguments = new HashMap<String, Object>();  
  23.         arguments.put(”x-dead-letter-exchange”“amq.direct”);  
  24.         arguments.put(”x-dead-letter-routing-key”“message_ttl_routingKey”);  
  25.         channel.queueDeclare(”delay_queue”truefalsefalse, arguments);  
  26.   
  27.         // 聲明隊列  
  28.         channel.queueDeclare(queue_name, truefalsefalsenull);  
  29.         // 綁定路由  
  30.         channel.queueBind(queue_name, ”amq.direct”“message_ttl_routingKey”);  
  31.   
  32.         String message = ”hello world!” + System.currentTimeMillis();  
  33.         // 設置延時屬性  
  34.         AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();  
  35.         // 持久性 non-persistent (1) or persistent (2)  
  36.         AMQP.BasicProperties properties = builder.expiration(”300000”).deliveryMode(2).build();  
  37.         // routingKey =delay_queue 進行轉發  
  38.         channel.basicPublish(”““delay_queue”, properties, message.getBytes());  
  39.         System.out.println(”sent message: ” + message + “,date:” + System.currentTimeMillis());  
  40.         // 關閉頻道和連接  
  41.         channel.close();  
  42.         connection.close();  
  43.     }  
  44. }  
package cn.slimsmart.study.rabbitmq.delayqueue.message;

import java.io.IOException;
import java.util.HashMap;

import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

public class Producer {

    private static String queue_name = "message_ttl_queue";

    public static void main(String[] args) throws IOException {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("10.1.199.169");
        factory.setUsername("admin");
        factory.setPassword("123456");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        HashMap<String, Object> arguments = new HashMap<String, Object>();
        arguments.put("x-dead-letter-exchange", "amq.direct");
        arguments.put("x-dead-letter-routing-key", "message_ttl_routingKey");
        channel.queueDeclare("delay_queue", true, false, false, arguments);

        // 聲明隊列
        channel.queueDeclare(queue_name, true, false, false, null);
        // 綁定路由
        channel.queueBind(queue_name, "amq.direct", "message_ttl_routingKey");

        String message = "hello world!" + System.currentTimeMillis();
        // 設置延時屬性
        AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();
        // 持久性 non-persistent (1) or persistent (2)
        AMQP.BasicProperties properties = builder.expiration("300000").deliveryMode(2).build();
        // routingKey =delay_queue 進行轉發
        channel.basicPublish("", "delay_queue", properties, message.getBytes());
        System.out.println("sent message: " + message + ",date:" + System.currentTimeMillis());
        // 關閉頻道和連接
        channel.close();
        connection.close();
    }
}
消費者:

  1. package cn.slimsmart.study.rabbitmq.delayqueue.message;  
  2.   
  3. import java.util.HashMap;  
  4.   
  5. import com.rabbitmq.client.Channel;  
  6. import com.rabbitmq.client.Connection;  
  7. import com.rabbitmq.client.ConnectionFactory;  
  8. import com.rabbitmq.client.QueueingConsumer;  
  9.   
  10. public class Consumer {  
  11.   
  12.     private static String queue_name = “message_ttl_queue”;  
  13.   
  14.     public static void main(String[] args) throws Exception {  
  15.         ConnectionFactory factory = new ConnectionFactory();  
  16.         factory.setHost(”10.1.199.169”);  
  17.         factory.setUsername(”admin”);  
  18.         factory.setPassword(”123456”);  
  19.         Connection connection = factory.newConnection();  
  20.         Channel channel = connection.createChannel();  
  21.         HashMap<String, Object> arguments = new HashMap<String, Object>();  
  22.         arguments.put(”x-dead-letter-exchange”“amq.direct”);  
  23.         arguments.put(”x-dead-letter-routing-key”“message_ttl_routingKey”);  
  24.         channel.queueDeclare(”delay_queue”truefalsefalse, arguments);  
  25.   
  26.         // 聲明隊列  
  27.         channel.queueDeclare(queue_name, truefalsefalsenull);  
  28.         // 綁定路由  
  29.         channel.queueBind(queue_name, ”amq.direct”“message_ttl_routingKey”);  
  30.   
  31.         QueueingConsumer consumer = new QueueingConsumer(channel);  
  32.         // 指定消費隊列  
  33.         channel.basicConsume(queue_name, true, consumer);  
  34.         while (true) {  
  35.             // nextDelivery是一個阻塞方法(內部實現其實是阻塞隊列的take方法)  
  36.             QueueingConsumer.Delivery delivery = consumer.nextDelivery();  
  37.             String message = new String(delivery.getBody());  
  38.             System.out.println(”received message:” + message + “,date:” + System.currentTimeMillis());  
  39.         }  
  40.     }  
  41.   
  42. }  
package cn.slimsmart.study.rabbitmq.delayqueue.message;

import java.util.HashMap;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.QueueingConsumer;

public class Consumer {

    private static String queue_name = "message_ttl_queue";

    public static void main(String[] args) throws Exception {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("10.1.199.169");
        factory.setUsername("admin");
        factory.setPassword("123456");
        Connection connection = factory.newConnection();
        Channel channel = connection.createChannel();
        HashMap<String, Object> arguments = new HashMap<String, Object>();
        arguments.put("x-dead-letter-exchange", "amq.direct");
        arguments.put("x-dead-letter-routing-key", "message_ttl_routingKey");
        channel.queueDeclare("delay_queue", true, false, false, arguments);

        // 聲明隊列
        channel.queueDeclare(queue_name, true, false, false, null);
        // 綁定路由
        channel.queueBind(queue_name, "amq.direct", "message_ttl_routingKey");

        QueueingConsumer consumer = new QueueingConsumer(channel);
        // 指定消費隊列
        channel.basicConsume(queue_name, true, consumer);
        while (true) {
            // nextDelivery是一個阻塞方法(內部實現其實是阻塞隊列的take方法)
            QueueingConsumer.Delivery delivery = consumer.nextDelivery();
            String message = new String(delivery.getBody());
            System.out.println("received message:" + message + ",date:" + System.currentTimeMillis());
        }
    }

}

查看資料:

http://www.rabbitmq.com/ttl.html 
http://www.rabbitmq.com/dlx.html 
http://www.rabbitmq.com/maxlength.html
https://www.cloudamqp.com/docs/delayed-messages.html 

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