【Azure 應用服務】由 Azure Functions runtime is unreachable 的錯誤消息推導出 ASYNC(非同步)和 SYNC(同步)混用而引起ThreadPool耗盡問題
- 2021 年 6 月 5 日
- 筆記
- 【Azure 應用服務】, App Service, ASYNC中使用WAIT導致執行緒池耗盡, Azure Functions runtime is unreachable
問題描述
在Azure Function Portal上顯示: Azure Functions runtime is unreachable,引起的結果是Function App目前不工作,但是此前一直都是正常工作的,且沒有對Azure Function做過任何的改動,那它是為什麼出現這樣的問題呢?
問題分析
Azure Functions runtime is unreachable 的錯誤是 」Azure Functions 運行時不可訪問」,此問題的最常見原因是函數應用失去了對其存儲帳戶的訪問許可權。首先我們根據官方文檔( 排查錯誤:「Azure Functions 運行時不可訪問」 )排查以下每一點:
-
存儲帳戶已被刪除
-
存儲帳戶應用程式設置已被刪除
-
存儲帳戶憑據無效
-
存儲帳戶不可訪問
-
每日執行配額已滿
-
應用受防火牆保護
註:多個Function App之間應盡量避免共享Storage Account,在創建Function App的時候,需要關聯獨立的Storage Account.
在排查外以上每一點後,如果依舊出現 「Azure Functions runtime is unreachable」的問題,那麼此時就需要分析當前所運行的Function是否由異常,是否由出現CPU 100%, Memory 100%,以及執行緒數等情況。
在這次的問題中,在Azure Function的日誌中,發現大量的如下兩種異常:
異常一 |
Microsoft.Azure.ServiceBus.MessageLockLostException : The lock supplied is invalid. Either the lock expired, or the message has already been removed from the queue. Reference:ae0230de-86a9-xxxx-fdd, TrackingId:eaeef_xxxxxxxxxxxxxxxxxxxx0_B17, SystemTracker:api-11:Topic:inmessage, Timestamp:2021-06-02T06:21:22 at async Microsoft.Azure.ServiceBus.Core.MessageReceiver.OnRenewLockAsync(String lockToken) ….. at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at async Microsoft.Azure.ServiceBus.MessageReceivePump.RenewMessageLockTask(Message message,CancellationToken renewLockCancellationToken) |
與Service Bus相關 |
異常二 |
Microsoft.Azure.Storage.StorageException : The lease ID specified did not match the lease ID for the blob. at async Microsoft.Azure.Storage.Core.Executor.Executor.ExecuteAsync[T](RESTCommand`1 cmd,IRetryPolicy policy,OperationContext operationContext,CancellationToken token) at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() …. at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at async Microsoft.Azure.WebJobs.Host.Timers.TaskSeriesTimer.RunAsync(CancellationToken cancellationToken) at C:\projects\azure-webjobs-sdk-rqm4t\src\Microsoft.Azure.WebJobs.Host\Timers\TaskSeriesTimer.cs : 147 |
與Storage Account相關 |
在異常資訊中,並沒有明確的指明當前Azure Funciton運行不正常及Azure Functions runtime is unreachable有關的資訊。但是Service Bus的 MessageLockLostException 異常值得重點分析,因為MessageLockLostException 異常意味著Function在消費Service Bus的消息的時候,由於處理消息的時間過長,超過了Meesage Lock(鎖定,默認30秒),消息無法消費使得回滾會Service Bus的隊列中,然後進行下一輪的消費,如此往返。最終表現就是Azure Funciton運行不正常。為了驗證這一步,需要收集Function的DUMP文件。PS: 如何獲取Windows下Function的DUMP,可參考博文:快速獲取DUMP文件(App Service for Windows(.NET/.NET Core): //www.cnblogs.com/lulight/p/13574331.html
檢查記憶體DUMP,確認大量執行緒(基本上是所有執行緒)都在等待執行 調用HttpClient上傳這一步。而執行緒池此時已耗盡,無法創建新的執行緒繼續完成Http請求。導致Azure Function運行不正常,無法消費Service Bus中的消息,也引起了 Microsoft.Azure.ServiceBus.MessageLockLostException 異常
(收集DUMP後可使用Visual Studio 2019查看DUMP中的Stack資訊)
綜上,根據DUMP文件中的發現,找到了問題原因:應用程式程式碼在非同步程式碼調用中使用了Wait方法,導致在Service Bus消息的高峰期間,進程池耗盡,無法正常運行並處理消息,也引起 Azure Functions runtime is unreachable,在最佳的操作要求中,有明確的要求:
「在 C# 中,請始終避免引用 Result
屬性或在 Task
實例上調用 Wait
方法。 這種方法會導致執行緒耗盡。」
解決辦法
一:從執行緒池耗盡的方面入手,增加ThreadPool。在FunctionApp上更改平台配置,改為按照64位模式運行。由於32位的程式(x86)的Function最大執行緒數位125,而64位修改後,ThreadPool最大值提升到32767。(如果應用打包時target的x86,則需要重新打包應用target x64)。
二:修改程式碼。移除在async方法中所調用的wait()方法,修改位await。PS: 使用非同步程式碼時,裡面應該一路使用async和await。
參考資料
排查錯誤:「Azure Functions 運行時不可訪問」: //docs.azure.cn/zh-cn/azure-functions/functions-recover-storage-account
提高 Azure Functions 的性能和可靠性的最佳做法: //docs.azure.cn/zh-cn/azure-functions/functions-best-practices#avoid-sharing-storage-accounts
【完】