一、前言
本文演示的延遲隊列是基於SpringBoot整合RabbitMQ來實現的,關於SpringBoot整合RabbitMQ可參考該鏈接:https://blog.csdn.net/pkxwyf/article/details/105158608
二、延遲隊列概述和原理
2.1、什麼是延遲隊列
所謂延遲隊列,即消息進入隊列後不會立即被消費,只有到達指定時間後,纔會被消費。
2.1、延遲隊列應用場景
- 用戶下單後,30分鐘未支付,取消訂單恢復庫存。
2.3、延遲隊列實現原理
很可惜的是在RabbitMQ中並未提供延遲隊列功能,但是可以使用:TTL+死信隊列 組合實現延遲隊列的效果
三、延遲隊列代碼演示
3.1、消息生產方代碼實現
1、編寫配置類:定義交換機和隊列信息
@Configuration
public class RabbitMQConfig {
// 普通交換機名稱
public static final String EXCHANGE_NAME = "order_topic_exchange";
// 普通隊列名稱
public static final String QUEUE_NAME = "order_queue";
// 死信交換機名稱
public static final String DEAD_EXCHANGE_NAME = "dead_order_topic_exchange";
// 死信隊列名稱
public static final String DEAD_QUEUE_NAME = "dead_order_queue";
// 1. 定義普通交換機
@Bean("orderExchange")
public Exchange createExchange(){
return ExchangeBuilder.topicExchange(EXCHANGE_NAME).durable(true).build();
}
// 2. 定義普通隊列
@Bean("orderQueue")
public Queue createQueue(){
// 創建map集合:封裝隊列參數
Map<String,Object> map = new HashMap<>();
// 設置死信交換機名稱
map.put("x-dead-letter-exchange", DEAD_EXCHANGE_NAME);
// 設置發送給死信交換機的routingKey
map.put("x-dead-letter-routing-key", "dead.order.news");
// 設置隊列過期時間:單位毫秒
map.put("x-message-ttl", 10000);
return QueueBuilder.durable(QUEUE_NAME).withArguments(map).build();
}
// 3. 定義死信交換機
@Bean("deadOrderExchange")
public Exchange createDeadExchange(){
return ExchangeBuilder.topicExchange(DEAD_EXCHANGE_NAME).durable(true).build();
}
// 4. 定義死信隊列
@Bean("deadOrderQueue")
public Queue createDeadQueue(){
// 創建map集合:封裝隊列參數
return QueueBuilder.durable(DEAD_QUEUE_NAME).build();
}
// 5. 隊列與交換機綁定關係
@Bean
public Binding bindExchangeAndQueue(@Qualifier("orderQueue") Queue queue,
@Qualifier("orderExchange") Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("order.#").noargs();
}
// 6. 死信隊列與死信交換機綁定關係
@Bean
public Binding bindDeadExchangeAndQueue(@Qualifier("deadOrderQueue") Queue queue,
@Qualifier("deadOrderExchange") Exchange exchange){
return BindingBuilder.bind(queue).to(exchange).with("dead.order.#").noargs();
}
}
2、編寫測試類:發送消息到MQ中
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestDelayQueue {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* 測試延遲隊列
*/
@Test
public void testSendStr() throws Exception{
// 發送消息
// 參數1:交換機名字
// 參數2:路由鍵
// 參數3:消息字符串
rabbitTemplate.convertAndSend(
"order_topic_exchange",
"order.news",
"用戶下單成功,訂單ID:1");
}
}
3.2、消息消費方代碼實現
@Component
public class RabbitMQConfig {
/**
* 監聽mq消息
*/
@RabbitListener(bindings = @QueueBinding(
exchange = @Exchange(value = "dead_order_topic_exchange",type = "topic"),
value = @Queue(value = "dead_order_queue",durable = "true"),
key = "dead.order.#"
))
public void handlerMessage(Message message, Channel channel)throws IOException {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
// 1. 轉換消息
System.out.println(new String(message.getBody()));
// 2. 業務處理
System.out.println("執行業務處理...");
System.out.println("根據訂單id查詢狀態...");
System.out.println("判斷是否支付成功...");
System.out.println("取消訂單,回滾庫存...");
// 3. 手工簽收
channel.basicAck(deliveryTag,true);
} catch (Exception e) {
// 4. 拒絕簽收
channel.basicNack(deliveryTag,true,false);
}
}
}
四、延遲隊列小結
- 延遲隊列指消息進入隊列後,可以被延遲一定時間再進行消費。
- RabbitMQ沒有提供延遲隊列功能,但可以使用:TTL+DLX實現延遲隊列效果。