大型分佈式微服務雲架構及電子商務平臺都離不開RabbitMQ消息中間件,我們的電子商務平臺中,在不同的業務應用場景下就使用消息中間件,今天我對消息中間件的死信隊列做一下總結,希望能夠幫助到更多的朋友,總結如下:
死信隊列介紹
- 死信隊列:DLX,
dead-letter-exchange
- 利用DLX,當消息在一個隊列中變成死信
(dead message)
之後,它能被重新publish到另一個Exchange,這個Exchange就是DLX
消息變成死信有以下幾種情況
- 消息被拒絕(basic.reject / basic.nack),並且requeue = false
- 消息TTL過期
- 隊列達到最大長度
死信處理過程
- DLX也是一個正常的Exchange,和一般的Exchange沒有區別,它能在任何的隊列上被指定,實際上就是設置某個隊列的屬性。
- 當這個隊列中有死信時,RabbitMQ就會自動的將這個消息重新發布到設置的Exchange上去,進而被路由到另一個隊列。
- 可以監聽這個隊列中的消息做相應的處理。
死信隊列設置
- 首先需要設置死信隊列的exchange和queue,然後進行綁定:
Exchange: dlx.exchange
Queue: dlx.queue
RoutingKey: #
#表示只要有消息到達了Exchange,那麼都會路由到這個queue上
- 然後需要有一個監聽,去監聽這個隊列進行處理
- 然後我們進行正常聲明交換機、隊列、綁定,只不過我們需要在隊列加上一個參數即可:
arguments.put(" x-dead-letter-exchange","dlx.exchange");
,這樣消息在過期、requeue、 隊列在達到最大長度時,消息就可以直接路由到死信隊列!
死信隊列演示
1. 消息生產者
public class Producer {
public static void main(String[] args) throws Exception {
//1 創建ConnectionFactory
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.43.157");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
//2 獲取Connection
Connection connection = connectionFactory.newConnection();
//3 通過Connection創建一個新的Channel
Channel channel = connection.createChannel();
String exchange = "test_dlx_exchange";
String routingKey = "dlx.save";
String msg = "Hello RabbitMQ DLX Message";
AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder()
.deliveryMode(2)
.contentEncoding("UTF-8")
.expiration("10000")
.build();
//發送消息
channel.basicPublish(exchange, routingKey, true, properties, msg.getBytes());
}
}
2. 自定義消費者
public class MyConsumer extends DefaultConsumer {
public MyConsumer(Channel channel) {
super(channel);
}
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.err.println("-----------consume message----------");
System.err.println("consumerTag: " + consumerTag);
System.err.println("envelope: " + envelope);
System.err.println("properties: " + properties);
System.err.println("body: " + new String(body));
}
}
3. 消費端
- 聲明正常處理消息的交換機、隊列及綁定規則
- 在正常交換機上指定死信發送的Exchange
- 聲明死信交換機、隊列及綁定規則
- 監聽死信隊列,進行後續處理,這裏省略
public class Consumer {
public static void main(String[] args) throws Exception {
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.43.157");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("/");
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
// 聲明一個普通的交換機 和 隊列 以及路由
String exchangeName = "test_dlx_exchange";
String routingKey = "dlx.#";
String queueName = "test_dlx_queue";
channel.exchangeDeclare(exchangeName, "topic", true, false, null);
//指定死信發送的Exchange
Map<String, Object> agruments = new HashMap<String, Object>();
agruments.put("x-dead-letter-exchange", "dlx.exchange");
//這個agruments屬性,要設置到聲明隊列上
channel.queueDeclare(queueName, true, false, false, agruments);
channel.queueBind(queueName, exchangeName, routingKey);
//要進行死信隊列的聲明
channel.exchangeDeclare("dlx.exchange", "topic", true, false, null);
channel.queueDeclare("dlx.queue", true, false, false, null);
channel.queueBind("dlx.queue", "dlx.exchange", "#");
channel.basicConsume(queueName, true, new MyConsumer(channel));
}
}
運行說明
啓動消費端,此時查看管控臺,新增了兩個Exchange,兩個Queue。在test_dlx_queue
上我們設置了DLX,也就代表死信消息會發送到指定的Exchange上,最終其實會路由到dlx.queue
上。
test_dlx_queue
的值爲1,而
dlx_queue
的值爲0。
10s後的隊列結果如圖,由於生產端發送消息時指定了消息的過期時間爲10s,而此時沒有消費端進行消費,消息便被路由到死信隊列中。
實際環境我們還需要對死信隊列進行一個監聽和處理,當然具體的處理邏輯和業務相關,這裏只是簡單演示死信隊列是否生效。希望能夠幫助更多的朋友瞭解RabbitMQ死信隊列!