訊息可能會傳遞失敗(例如,如果用來消費它們的交易式會話被回滾)。這樣的訊息會回到它的佇列中,準備重新傳遞。然而,這表示一個訊息可能不斷地被傳遞而沒有成功,從而無限期地留在佇列中,阻塞系統。
有 2 種方法可以處理這些未傳遞的訊息
-
延遲重新傳遞。
可以延遲訊息的重新傳遞。這讓客戶端有一些時間從任何暫時性故障中恢復,並防止其網路或 CPU 資源過載。
-
死信地址。
也可以設定死信地址,以便在指定的傳遞失敗次數後,訊息會從它們的佇列中移除並發送到死信地址。這些訊息將不會從此佇列再次傳遞。
這兩個選項可以結合使用,以獲得最大的靈活性。
1. 延遲重新傳遞
在客戶端經常失敗或回滾的情況下,延遲重新傳遞通常很有用。如果沒有延遲重新傳遞,系統可能會進入「抖動」狀態,即嘗試傳遞、客戶端回滾,然後在短時間內不斷地重新嘗試傳遞,消耗寶貴的 CPU 和網路資源。
#持久化重新傳遞
每次發生重新傳遞時,都會儲存兩個日誌更新記錄。一個用於發生的傳遞次數,另一個用於使用排程重新傳遞的情況。
建議在重新傳遞延遲非常短的情況下,保持 max-redelivery-records=1,因為這樣會在日誌上產生不必要的記錄。
1.1. 設定延遲重新傳遞
延遲重新傳遞在地址設定配置中定義
<!-- delay redelivery of messages for 5s -->
<address-setting match="exampleQueue">
<!-- default is 1.0 -->
<redelivery-delay-multiplier>1.5</redelivery-delay-multiplier>
<!-- default is 0 (no delay) -->
<redelivery-delay>5000</redelivery-delay>
<!-- default is 0.0) -->
<redelivery-collision-avoidance-factor>0.15</redelivery-collision-avoidance-factor>
<!-- default is redelivery-delay * 10 -->
<max-redelivery-delay>50000</max-redelivery-delay>
</address-setting>
如果指定了 redelivery-delay
,Apache ActiveMQ Artemis 將在重新傳遞訊息之前等待此延遲時間。
預設情況下,沒有重新傳遞延遲(redelivery-delay
設定為 0)。
其他後續訊息將會定期傳遞,只有被取消的訊息會在延遲後非同步地送回佇列。
您可以指定一個乘數(redelivery-delay-multiplier
),它將在 redelivery-delay
之上生效。每次重新傳遞訊息時,延遲時間將等於先前的延遲 * redelivery-delay-multiplier
。可以設定 max-redelivery-delay
以防止延遲變得太大。max-redelivery-delay
預設為 redelivery-delay
* 10。
範例
-
redelivery-delay=5000, redelivery-delay-multiplier=2, max-redelivery-delay=15000, redelivery-collision-avoidance-factor=0.0
-
傳遞嘗試 1. (不成功)
-
等待延遲時間:5000
-
傳遞嘗試 2. (不成功)
-
等待延遲時間:10000 // (5000 * 2) < max-delay-period。使用 10000
-
傳遞嘗試 3:(不成功)
-
等待延遲時間:15000 // (10000 * 2) > max-delay-period:使用 max-delay-delivery
-
可以使用地址萬用字元來設定一組地址的重新傳遞延遲(請參閱了解萬用字元語法),因此您不必為每個地址單獨指定重新傳遞延遲。
也可以透過設定 redelivery-collision-avoidance-factor
來修改 redelivery-delay
。此因素將隨機設為正或負,以控制最終值是否會增加或減少 redelivery-delay
。然後將其乘以 0.0 到 1.0 之間的隨機數。然後將此結果乘以 redelivery-delay
,然後將其加到 redelivery-delay
上,以得出最終值。
此演算法聽起來可能很複雜,但底線很簡單:您選擇的 redelivery-collision-avoidance-factor
越大,redelivery-delay
的變異就越大。redelivery-collision-avoidance-factor
必須介於 0.0 和 1.0 之間。
範例
-
redelivery-delay=1000, redelivery-delay-multiplier=1, max-redelivery-delay=15000, redelivery-collision-avoidance-factor=0.5, (使用
java.util.Random
選擇粗體值)-
傳遞嘗試 1. (不成功)
-
等待延遲時間:875 // 1000 + (1000 * ((0.5 * -1) * .25))
-
傳遞嘗試 2. (不成功)
-
等待延遲時間:1375 // 1000 + (1000 * ((0.5 * 1) * .75))
-
傳遞嘗試 3:(不成功)
-
等待延遲時間:975 // 1000 + (1000 * ((0.5 * -1) * .05))
-
當同一個佇列上有多個消費者,且所有消費者都以交易方式與同一個外部系統(例如,資料庫)互動時,此功能特別有用。如果訊息中有重疊的資料被同時消費,則一筆交易可能會成功,而其餘所有交易都會失敗。如果這些失敗的訊息在同一時間重新傳遞,則一個消費者成功而其餘消費者失敗的過程將會繼續。透過隨機以少量可設定的量填充重新傳遞延遲,可以避免這些重新傳遞「衝突」。
2. 死信地址
為了防止客戶端無限次地收到相同的未傳遞訊息(無論導致傳遞失敗的原因是什麼),訊息系統定義了死信地址:在指定的傳遞失敗次數後,訊息會從其佇列中移除,並發送到死信地址。
任何此類訊息都可以被轉移到佇列中,系統管理員可以稍後在這些佇列中檢視這些訊息,以採取行動。
Apache ActiveMQ Artemis 的地址可以被分配一個死信地址。一旦訊息傳遞失敗的次數達到給定的嘗試次數,它們就會從其佇列中移除,並發送到相關的死信地址。這些死信訊息可以稍後從死信地址中消費,以進行進一步檢查。
2.1. 設定死信地址
死信地址在地址設定配置中定義
<!-- undelivered messages in exampleQueue will be sent to the dead letter address
deadLetterQueue after 3 unsuccessful delivery attempts -->
<address-setting match="exampleQueue">
<dead-letter-address>deadLetterAddress</dead-letter-address>
<max-delivery-attempts>3</max-delivery-attempts>
</address-setting>
如果未指定 dead-letter-address
,則訊息將在 max-delivery-attempts
次傳遞失敗嘗試後被移除。
預設情況下,訊息最多會重新傳遞 10 次。將 max-delivery-attempts
設定為 -1 可進行無限次重新傳遞。
可以為一組符合條件的地址全域設定 死信地址
,您可以為特定地址設定將 max-delivery-attempts
設定為 -1,以僅允許此地址進行無限次重新傳遞。
可以使用地址萬用字元來設定一組地址的死信設定(請參閱了解萬用字元語法)。
2.3. 自動建立死信資源
通常會根據未傳遞訊息的原始地址來隔離未傳遞訊息。例如,發送到 stocks
地址的訊息由於某種原因無法傳遞,最終可能會被路由到 DLQ.stocks
佇列,同樣,發送到 orders
地址的訊息無法傳遞可能會被路由到 DLQ.orders
佇列。
使用這種模式可以輕鬆追蹤和管理未傳遞的訊息。但是,這在主要使用自動建立的地址和佇列的環境中可能會帶來挑戰。通常,這些環境中的管理員不希望手動建立 address-setting
來設定 dead-letter-address
,更不用說實際的 address
和 queue
來保留未傳遞的訊息了。
此問題的解決方案是將 auto-create-dead-letter-resources
address-setting
設定為 true
(預設為 false
),以便代理自動建立 address
和 queue
來處理未傳遞的訊息。建立的 address
將是 dead-letter-address
定義的那個。將在此 address
上建立一個 MULTICAST
queue
。它將以訊息先前發送到的 address
命名,並且將使用屬性 _AMQ_ORIG_ADDRESS
定義篩選器,以便它僅接收發送到相關 address
的訊息。queue
名稱可以使用前綴和後綴進行設定。請參閱下表中的相關設定
地址設定 |
預設 |
---|---|
|
|
|
(空字串) |
以下是一個範例設定
<address-setting match="#">
<dead-letter-address>DLA</dead-letter-address>
<max-delivery-attempts>3</max-delivery-attempts>
<auto-create-dead-letter-resources>true</auto-create-dead-letter-resources>
<dead-letter-queue-prefix></dead-letter-queue-prefix> <!-- override the default -->
<dead-letter-queue-suffix>.DLQ</dead-letter-queue-suffix>
</address-setting>
可以使用佇列本身的名稱(例如,使用核心用戶端時)或使用完全合格的佇列名稱(例如,使用 JMS 用戶端時)直接存取保留未傳遞訊息的佇列,就像任何其他佇列一樣。另請注意,該佇列是自動建立的,這表示它將按照相關的 address-settings
自動刪除。
3. 傳遞計數持久化
在正常使用情況下,Apache ActiveMQ Artemis 不會持久更新傳遞計數,直到訊息被回滾(即傳遞計數不會在訊息傳遞給消費者之前更新)。在大多數訊息使用案例中,訊息會在消費後立即被消費、確認和忘記。在這種情況下,在傳遞訊息之前持久更新傳遞計數會為每個傳遞的訊息增加一個額外的持久步驟,這意味著顯著的效能損失。
但是,如果在訊息傳遞發生之前未持久更新傳遞計數,則在伺服器崩潰的情況下,訊息可能已被傳遞,但這不會反映在傳遞計數中。在恢復階段,伺服器將不知道這一點,並會傳遞 redelivered
設定為 false
的訊息,而它應該是 true
。
由於此行為違反了嚴格的 JMS 語義,Apache ActiveMQ Artemis 允許在訊息傳遞之前持久化傳遞計數,但由於效能影響,預設情況下此功能已停用。
要啟用它,請在 broker.xml
中將 persist-delivery-count-before-delivery
設定為 true
<persist-delivery-count-before-delivery>true</persist-delivery-count-before-delivery>