【Azure 应用服务】由 Azure Functions runtime is unreachable 的错误消息推导出 ASYNC(异步)和 SYNC(同步)混用而引起ThreadPool耗尽问题

问题描述

在Azure Function Portal上显示: Azure Functions runtime is unreachable,引起的结果是Function App目前不工作,但是此前一直都是正常工作的,且没有对Azure Function做过任何的改动,那它是为什么出现这样的问题呢?

 

问题分析

Azure Functions runtime is unreachable 的错误是 ”Azure Functions 运行时不可访问”,此问题的最常见原因是函数应用失去了对其存储帐户的访问权限。首先我们根据官方文档( 排查错误:“Azure Functions 运行时不可访问” )排查以下每一点:

  1. 存储帐户已被删除

  2. 存储帐户应用程序设置已被删除

  3. 存储帐户凭据无效

  4. 存储帐户不可访问

  5. 每日执行配额已满

  6. 应用受防火墙保护

 注:多个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)https://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 运行时不可访问”: https://docs.azure.cn/zh-cn/azure-functions/functions-recover-storage-account

提高 Azure Functions 的性能和可靠性的最佳做法: https://docs.azure.cn/zh-cn/azure-functions/functions-best-practices#avoid-sharing-storage-accounts

 

【完】

posted @ 2021-06-05 23:14  路边两盏灯  阅读(307)  评论(0编辑  收藏  举报