Rabbitmq的死信

以往清泉發表於2021-11-20

一、概述

死信有死信佇列、死信交換器和死信訊息組成。死信訊息則有如下三種情況生成:

1.消費者使用basic.reject或 basic.nack並將requeue引數設定為false來拒絕該訊息

2.訊息設定了TTL過期時間,過期時間內沒有消費導致過期

3.訊息因超過佇列長度導致被丟棄

如果佇列刪除或者佇列的TTL過期時間到了被刪除其中的訊息是不會成為死信訊息的。

每個佇列可通過佇列引數arg或者策略policy設定死信交換器和死信路由鍵來處理死信訊息,當佇列引數與策略都有設定時,以佇列引數設定內容為準,建議使用策略。沒有設定死信,訊息滿足死信條件將會被丟棄。

簡而言之,一個佇列A設定了死信交換器和路由鍵,當一個訊息滿足死信要求時會通過設定的死信交換器和路由鍵找到對應的佇列將訊息傳遞到對應佇列B中,B也就是對應的死信佇列。所以,死信交換機和死信佇列都是正常的交換器和佇列,和其他的交換器佇列宣告沒區別。通過死信佇列可以防止訊息的意外丟失,保證重要的訊息被執行記錄。

二、在策略中設定死信

我們通過rabbitmqctl命令建立一個名為DLX的設定了死信交換器為my-dlx路由鍵為my-dlx-routekey的策略,對應用於所有的佇列。

 rabbitmqctl set_policy --vhost my_vhost1 DLX ".*" '{"dead-letter-exchange":"my-dlx","dead-letter-routing-key":"my-dlx-routekey"}' --apply-to queues

或者在web管理頁中新增policy如下:

 

 有多個策略的話使用優先順序最高的策略,設定後佇列會有個DLX的特性。

三、在佇列引數中設定死信

我們通過佇列引數設定x-dead-letter-exchange和x-dead-letter-routing-key,死信交換器必須和佇列處於同一個vhost下,示例程式碼如下:

Dictionary<string, object> arg = new Dictionary<string, object>();
arg.Add("x-dead-letter-exchange","hello-dlx");
arg.Add("x-dead-letter-routing-key", "hello-dlx-routekey");
channel.QueueDeclare(
  "HelloQueue",//佇列名稱
  false,       //是否持久化
  false,       //是否只對首次宣告的佇列可見
  false,       //是否自動刪除
  arg         ////關於佇列和佇列內訊息的詳細設定,鍵值對形式
);

四、死信路由

我們在上述程式碼中都設定了死信路由鍵,但是其實是可以不設定的,如果設定了就遵循設定的路由鍵,如果沒有設定則遵循原交換器的路由鍵。比如:我們將訊息傳送到路由鍵為A的交換器進入佇列,則當訊息死信時也傳送到路由鍵為A的死信交換器,如果設定了x-dead-letter-routing-key為B則傳送到路由鍵為B的死信交換器中。如果不設定死信路由鍵,那麼由CC和BCC設定的路由鍵也會觸發,CC和BCC相當於抄送和密送到別的交換器。

如果產生死信迴圈,既到達同一個佇列兩次,訊息將會被拋棄。當死信訊息傳送到死信佇列後將會從原佇列刪除,避免過多訊息的積壓,但是如果目標的死信佇列不能接受訊息,則該死信訊息將可能丟失。

五、死信訊息

當死信訊息被髮送到死信佇列後會有以下變化:

1.訊息的交換器名稱會改為死信交換器名稱;

2.設定了死信路由鍵的話,路由鍵會相應更改;

3.CC和BCC標頭將會刪除;

死信訊息將會新增一些標頭,我們先看下列印的標頭:

{"Headers":{
        "x-death":[
            {
                "count":1,
                "reason":"ZXhwaXJlZA==",
                "queue":"SGVsbG9RdWV1ZQ==",
                "time":{
                    "UnixTime":1637409202
                },
                "exchange":"SGVsbG9FeGNoYW5nZQ==",
                "routing-keys":[
                    "aG9sYQ=="
                ]
            }
        ],
        "x-first-death-exchange":"SGVsbG9FeGNoYW5nZQ==",
        "x-first-death-queue":"SGVsbG9RdWV1ZQ==",
        "x-first-death-reason":"ZXhwaXJlZA=="
    }
}

可以看到值是Base64格式的,新新增了x-death、x-first-death-exchange、x-first-death-queue、x-first-death-reason四個值。

x-first-death-exchange:第一次的成為死信時的交換器名稱;

x-first-death-queue:第一次的成為死信時的佇列名稱;

x-first-death-reason:第一次的成為死信時的原因,死亡原因分為四種下同分別是:rejected(訊息處理被拒絕)、expired (ttl時間到期)、maxlen(超過佇列允許最大長度)和Delivery-limit(訊息返回的次數超限額)

這三個是第一次死信的時候新增的,後面就不會變了。

x-death裡面是陣列形式的,因為訊息可能多次死信。新條目被新增到x-death 陣列的開頭如果x-death已經包含一個具有相同佇列和死字原因的條目,它的計數字段count將增加,並將移到陣列的開頭。

queue:訊息在死信之前所在佇列的名稱;

reason:死信的原因,同上;

time:訊息被死信的日期和時間,為 64 位 AMQP 0-9-1 時間戳;

exchange :訊息釋出到的交換器名(請注意,如果訊息多次死信,這將是死信交換);

routing-keys :訊息釋出時使用的路由鍵(包括CC金鑰,但不包括BCC金鑰 );

count : 由於這個原因,這條訊息在這個佇列中被死信多少次;

original-expiration(如果訊息由於訊息的TTL死信的):訊息的原始到期屬性。該到期屬性從死刻字的訊息,以防止它被路由到任何佇列再次到期刪除。

學習連結:https://www.rabbitmq.com/dlx.html

相關文章