前言
經過上一篇的介紹,相信大家對RabbitMQ 的各種概念有了一定的瞭解,及如何使用RabbitMQ.Client 去傳送和消費訊息。
特性及使用場景
1. TTL 過期時間
TTL可以用來指定queue 和message多久會被去掉;在短期message數量很大,或者訂單需要特定失效(例如15min支付)等場景,設定訊息的過期時間可以減輕rabbitmq的壓力,後者可以幫助方便的實現業務。
那麼如何設定訊息過期時間呢?
- 為queue中的訊息整體設定
var ttlSetting = new Dictionary<string, Object>(); ttlSetting.Add("x-message-ttl", 10000); queueName = _channel.QueueDeclare(arguments: ttlSetting).QueueName;
- 為每條message設定過期時間
var properties = _channel.CreateBasicProperties(); properties.Expiration = "10000"; var messageBytes = ObjectToByteArray(message); _channel.BasicPublish(TicketExchangeName, optType, false, properties, messageBytes);
佇列TTL 設定
我們不但可以對message 設定過期時間,還可以對訊息佇列設定生存時間,當超過設定時間未被使用,該訊息佇列可以被自動刪除。
設定方法同樣是在宣告佇列的時候傳入引數即可:
var ttlSetting = new Dictionary<string, Object>(); ttlSetting.Add("x-expires", 6000); queueName = _channel.QueueDeclare(arguments: ttlSetting).QueueName;
2. 死信交換器和死信佇列
何為死信?
- 被拒絕的訊息
- 過期的訊息
- 訊息佇列達到最大長度
當死信發生時,死信會通過死信交換機進入到宣告的死信佇列,可以通過引數宣告:
_channel.ExchangeDeclare(DLExchangeName, ExchangeType.Direct); var arguments = new Dictionary<string, Object>(); arguments.Add("x-dead-letter-exchange", DLExchangeName); _channel.QueueDeclare("dlqueue", arguments: arguments);
死信訊息的路由,如果在訊息初始被投送到的佇列上設定了 x-dead-letter-routing-key 引數,則死信訊息路由時以這個為準,否則按照它被產生時的路由key為準。
例如,如果您將訊息釋出到具有路由金鑰 foo 的交換,並且該訊息是死信的,它將被髮布到其具有路由金鑰 foo 的死信交換。如果訊息最初登陸的佇列已宣告為 x-dead-letter-routing-key 設定為 bar,則訊息將釋出到其帶有路由鍵 bar 的死信交換。
檢視死信佇列中的訊息可以幫助我們瞭解分析程式的執行健康情況,明確異常發生的原因。
3. 交換機,佇列,訊息的持久化
交換機的持久化是我們在使用rabbitmq經常需要做的事情,宣告交換器時將 durable 引數設定為 true 來實現的。如果不設定持久化屬性的話,當 RabbitMQ 服務重啟後交換器的資料就會丟失,需要注意的是,是交換器的資料丟失,訊息不會丟失,只是不能將訊息傳送到這個交換器中了,一般生產環境使用都會把該屬性設定為持久化。
佇列的持久化:先介紹佇列的持久化,是因為訊息的持久化是依賴在佇列持久化之上的。通過在宣告時將durable 引數設定為 true 達到目的。
_channel.QueueDeclare(“myqueue”, true, false, arguments: ttlSetting)
訊息的持久化:通過在publish 訊息時,設定基本屬性 DeliveryMode 來實現訊息的永續性。
var properties = _channel.CreateBasicProperties(); properties.DeliveryMode = 2;
4. 訊息的確認機制
相比於http請求和RPC,message queue的應用確實帶來了不少便利,但引入新的application不可避免的增加了可靠性的考慮;如何確保訊息從生產者準確的投遞到了rabbitmq broker,又如何確保消費者真的消費了訊息,又或者消費者沒有重複消費訊息?
這些都是我們在使用訊息佇列時候要考慮的問題,如何你使用何種技術(RabbitMQ或者是Kafka)。
其中,在保證訊息的準確投遞和消費上,RabbitMQ提供了確認機制幫助我們確保訊息的可靠性。
生產端的訊息確認機制:
訊息確認的處理辦法分為:單個訊息同步確認(吞吐量低),批量訊息同步確認,訊息非同步確認。這裡只介紹非同步confirm的情況。
var channel = connection.CreateModel(); channel.ConfirmSelect(); channel.BasicAcks += (sender, ea) => { // code when message is confirmed }; channel.BasicNacks += (sender, ea) => { //code when message is nack-ed };
var outstandingConfirms = new ConcurrentDictionary<ulong, string>();
如果希望在投遞失敗後,重新投遞,可以用 ConcurrentDictionary 儲存deliverTag 和 訊息體,在投遞失敗後,重新投遞。
消費端的訊息確認機制:
1. 自動應答
消費者在消費訊息的時候,如果設定應答模式為自動,則消費者收到訊息後,訊息就會立即被 RabbitMQ 從 佇列中刪除掉。
因此,在實際開發者,我們基本上是將消費應答模式設定為手動確認更為妥當一些。
_channel.BasicConsume(queue: queueName, autoAck: true, consumer: consumer);
2. 手動應答
消費者在收到訊息後:
- 可以在既定的正常情況下進行確認(告訴 RabbitMQ,我已經消費過該訊息了,你可以刪除該條資料了);
- 可以在既定的異常情況下不進行確認(RabbitMQ 會繼續保留該條資料),這樣下一次可以繼續消費該條資料。
_channel.BasicAck(ea.DeliveryTag, false);
本文介紹了過期時間,死信佇列,應答機制和持久化的設定和使用場景,後續還會繼續介紹RabbitMQ的其它特性和叢集搭建,歡迎大家關注和提出意見。
---------------------------------------------------