使用場景介紹
1、發版時經常需要不停機發版,遇到mq消費者,消費一半停機就會出現消息丟失(沒有使用手動確認的情況)
2、例如支付場景,準時支付、超過未支付將執行不同的方案,其中超時未支付可以看做一個延時消息。
3、例如滴滴、淘寶的自動評價都是類似場景(不一定是用的什麼技術)
我是發版的情況遇到了
可能有人會問了,mq支持手動確認啊,爲什麼不使用確認機制呢?1、由於用的是Spring 的RabbitListener註解,無法使用手動確認機制
2、也是最主要的原因是我們使用mq的場景
(類似於假如我做一個事情需要兩步完成,每一步完成都會收錢。那麼就會出現,第一步完成後,停機了,假如使用消息重發就會造成浪費第一步完成的錢)
實現方式
開始想到的是發版時,可以將消息發到隊列裏,然後消費者不要馬上消費,等一定的時間再來消費這一消息。在網上找資料找到兩種實現方式:
1、使用插件
在rabbitmq 3.5.7及以上的版本提供了一個插件(rabbitmq-delayed-message-exchange)來實現延遲隊列,而我們公司mq是單獨管理的,所以這種方式直接pass2、使用兩個特性
AMQP和RabbitMQ本身沒有直接支持延遲隊列功能,但是可以通過以下特性模擬出延遲隊列的功能。但是我們可以通過RabbitMQ的兩個特性來曲線實現延遲隊列。
Time To Live(TTL)
RabbitMQ可以針對Queue和Message設置 x-message-tt,來控制消息的生存時間,如果超時,則消息變爲dead letterRabbitMQ針對隊列中的消息過期時間有兩種方法可以設置。
A: 通過隊列屬性設置,隊列中所有消息都有相同的過期時間。
B: 對消息進行單獨設置,每條消息TTL可以不同。
如果同時使用,則消息的過期時間以兩者之間TTL較小的那個數值爲準。消息在隊列的生存時間一旦超過設置的TTL值,就成爲dead letterDead Letter Exchanges(DLX)
RabbitMQ的Queue可以配置x-dead-letter-exchange 和x-dead-letter-routing-key(可選)兩個參數,如果隊列內出現了dead letter,則按照這兩個參數重新路由。x-dead-letter-exchange:出現dead letter之後將dead letter重新發送到指定exchange
x-dead-letter-routing-key:指定routing-key發送
隊列出現dead letter的情況有:
消息或者隊列的TTL過期
隊列達到最大長度
消息被消費端拒絕(basic.reject or basic.nack)並且requeue=false
利用DLX,當消息在一個隊列中變成死信後,它能被重新publish到另一個Exchange。這時候消息就可以重新被消費。
示例代碼
初始化過期隊列的代碼
@Bean
public Queue signQueueStore() {
Map<String, Object> arguments = new HashMap<>();
arguments.put("x-dead-letter-exchange", exchange);
arguments.put("x-dead-letter-routing-key", signKey);
arguments.put("x-message-ttl",300000);
Queue queue = new Queue(signQueueStore,true,false,false,arguments);
System.out.println("arguments :" + queue.getArguments());
return queue;
}
綁定過期隊列的代碼
@Bean
public Binding signStoreBinding() {
return BindingBuilder.bind(signQueueStore()).to(defaultExchange()).with(signKeyStore);
}
初始化正常隊列的代碼和綁定普通隊列的代碼
@Bean
public Queue signQueue() {
return new Queue(signQueue);
}
@Bean
public Binding signBinding(Queue signQueue, DirectExchange defaultExchange) {
/** 將隊列綁定到交換機 */
return BindingBuilder.bind(signQueue).to(defaultExchange).with(signKey);
}
其他發送與消費不變
在重啓時將消息發送到過期隊列,在重啓完成後,消息發送到正常隊列,過期隊列消息過期後會自動路由到正常隊列進行消費。(可以設置一個標誌,判斷該標誌位重啓狀態,則發到過期隊列,爲正常狀態發送到正常隊列)