【服務總線 Azure Service Bus】Service Bus在使用預提取(prefetching)後出現Microsoft.Azure.ServiceBus.MessageLockLostE

問題描述

Service Bus接收端的日誌中出現大量的MessageLockLostException異常。完整的錯誤消息爲:c#

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) 791windows

 

問題緣由

在接收方使用預提取後,Service Bus服務將鎖定這次預提取的消息經過鎖定操做,其餘接收方則沒法接收到此預提取的消息(保持消息惟一消費)。若是接收方在鎖定過時以前沒法完成此消息,則該消息便對其餘接收方可用。api

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

 

默認狀況下,消息鎖定在 30 秒後過時。 這一值可延長到 5 分鐘。 一般在建立隊列時進行設置。 這是隊列級別的屬性,不能在消息基礎上進行更改。以下圖中的Message lock duration(能夠點擊Change Link進行修改).async

解決問題

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

方法二:在設定消息CompleteAsync前,判斷時間 message.LockedUntilUtc中的時間是否已經超過了Message Lock Duration,若是消息未到期但即將到期,可經過調用RenewLock,延續和擴展又一默認鎖定時間段fetch

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

應用程序可能收到包含到期或即將到期的鎖定的消息。 若是是這樣,應用程序可能處理該消息,但隨後發現,因鎖定到期而沒法完成處理。 應用程序可查看 LockedUntilUtc 屬性(受代理時鐘和本地計算機時鐘之間的時鐘誤差約束)。 若是消息鎖定已到期,則應用程序必須忽略該消息,不該對該消息或經過該消息調用任何 API。 若是消息未到期但即將到期,可經過調用 message.RenewLock() 延續和擴展又一默認鎖定時間段spa

若是鎖定在預提取緩衝區靜默地到期,則視爲已放棄該消息,且可再次將消息用於從隊列進行檢索。 這可能致使將消息提取到預提取緩衝區,並置於末尾。 若是在消息過時期間每每沒法使用預提取緩存區,這將致使重複預提取消息,但始終沒法將其以可用(有效鎖定)狀態有效送達,並最終在超出最大傳送數後移動到死信隊列3d

擴展問題

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

相關文章
相關標籤/搜索