二、RabbitMQ 進階特性及使用場景 [.NET]

懶懶的佩奇發表於2021-07-04

前言

經過上一篇的介紹,相信大家對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的其它特性和叢集搭建,歡迎大家關注和提出意見。

---------------------------------------------------

相關文章