【服務匯流排 Azure Service Bus】Service Bus在使用預提取(prefetching)後出現Microsoft.Azure.ServiceBus.MessageLockLostException異常問題

路邊兩盞燈發表於2020-12-01

問題描述

Service Bus接收端的日誌中出現大量的MessageLockLostException異常。完整的錯誤訊息為:

Microsoft.Azure.ServiceBus.MessageLockLostException: The lock supplied is invalid. Either the lock expired, or the message has already been removed from the queue. Reference:b2b452db-bf32-41c1-8b76-e546fbdc3856, TrackingId:625929b5-7392-4bf2-9beb-63c132837fc8_B0, SystemTracker:nchn-pr-dep-iot-bus:Queue:dep-iot-input-st-output, Timestamp:2020-12-01T06:13:15
at Microsoft.Azure.ServiceBus.Core.MessageReceiver.OnRenewLockAsync(String lockToken)
at Microsoft.Azure.ServiceBus.Core.MessageReceiver.<>c__DisplayClass74_0.<<RenewLockAsync>b__0>d.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.Azure.ServiceBus.RetryPolicy.RunOperation(Func`1 operation, TimeSpan operationTimeout)
at Microsoft.Azure.ServiceBus.RetryPolicy.RunOperation(Func`1 operation, TimeSpan operationTimeout)
at Microsoft.Azure.ServiceBus.Core.MessageReceiver.RenewLockAsync(String lockToken)
at Microsoft.Azure.ServiceBus.Core.MessageReceiver.RenewLockAsync(Message message)
at Microsoft.Azure.ServiceBus.MessageReceivePump.RenewMessageLockTask(Message message, CancellationToken renewLockCancellationToken) 791

 

問題原因

在接收方使用預提取後,Service Bus服務將鎖定此次預提取的訊息通過鎖定操作,其他接收方則無法接收到此預提取的訊息(保持訊息唯一消費)。如果接收方在鎖定過期之前無法完成此訊息,則該訊息便對其他接收方可用。

預提取的訊息的副本則保留在快取中。 使用過期的快取副本的接收方會在嘗試完成該訊息時接收到一個異常(MessageLockLostException)。 

 

預設情況下,訊息鎖定在 30 秒後過期。 這一值可延長到 5 分鐘。 通常在建立佇列時進行設定。 這是佇列級別的屬性,不能在訊息基礎上進行更改。如下圖中的Message lock duration(可以點選Change Link進行修改).

【服務匯流排 Azure Service Bus】Service Bus在使用預提取(prefetching)後出現Microsoft.Azure.ServiceBus.MessageLockLostException異常問題

解決問題

方法一:修改Message Lock Duration的時間長度,最大可以修改到5分鐘。

【服務匯流排 Azure Service Bus】Service Bus在使用預提取(prefetching)後出現Microsoft.Azure.ServiceBus.MessageLockLostException異常問題

方法二:在設定訊息CompleteAsync前,判斷時間 message.LockedUntilUtc中的時間是否已經超過了Message Lock Duration,如果訊息未到期但即將到期,可通過呼叫RenewLock,延續和擴充套件又一預設鎖定時間段

if(message.LockedUntilUtc.Minute <= 1)
    message.RenewLock();

應用程式可能收到包含到期或即將到期的鎖定的訊息。 如果是這樣,應用程式可能處理該訊息,但隨後發現,因鎖定到期而無法完成處理。 應用程式可檢視 LockedUntilUtc 屬性(受代理時鐘和本地計算機時鐘之間的時鐘偏差約束)。 如果訊息鎖定已到期,則應用程式必須忽略該訊息,不應對該訊息或通過該訊息呼叫任何 API。 如果訊息未到期但即將到期,可通過呼叫 message.RenewLock() 延續和擴充套件又一預設鎖定時間段

如果鎖定在預提取緩衝區靜默地到期,則視為已放棄該訊息,且可再次將訊息用於從佇列進行檢索。 這可能導致將訊息提取到預提取緩衝區,並置於末尾。 如果在訊息過期期間往往無法使用預提取快取區,這將導致重複預提取訊息,但始終無法將其以可用(有效鎖定)狀態有效送達,並最終在超出最大傳送數後移動到死信佇列

擴充套件問題

1:既然預提取更快,為何不是預設選項?(https://docs.azure.cn/zh-cn/service-bus-messaging/service-bus-prefetch#if-it-is-faster-why-is-prefetch-not-the-default-option

預提取可加快訊息流程,方法是在應用程式請求訊息時及請求訊息前,準備好訊息用於本地檢索。 這種吞吐量提升是應用程式作者不得不明確作出的某種權衡的結果:

通過 ReceiveAndDelete 接收模式,預提取快取區獲取的所有訊息在佇列中不再可用,僅駐留在記憶體中預提取快取區,直到應用程式通過 Receive/ReceiveAsync 或 OnMessage/OnMessageAsync API 接收到它們 。 如果在應用程式接收到訊息前終止應用程式,這些訊息將丟失,且不可恢復。


在 PeekLock 接收模式下,提取到預提取快取區的訊息將以鎖定狀態進入快取區,並且將超時時鐘用於鎖定計時。 如果預提取快取區很大,且處理所需時間過長,以致訊息鎖定在駐留於預提取快取區,甚至應用程式還在處理訊息時就到期,可能出現一些令人困惑的事件要應用程式處理。 如MessageLockLostException

如果訊息處理需要高度的可靠性,且處理需要大量精力和時間,則建議謹慎使用或者絲毫不用預提取功能。

如果需要較高吞吐量且訊息處理通常比較便宜,則預提取會產生顯著的吞吐量優勢。

 

 

參考資料

Windows Azure MessageLockLostExceptionhttps://stackoverflow.com/questions/15303711/windows-azure-messagelocklostexception

使用服務匯流排訊息傳遞改進效能的最佳實踐https://docs.azure.cn/zh-cn/service-bus-messaging/service-bus-performance-improvements?tabs=net-standard-sdk#prefetching

 

相關文章