CLRThreadPoolQueue 中的大部分请求

Large percent of requests in CLRThreadPoolQueue

我们有一个 ASP.NET MVC 应用程序托管在 Azure 应用程序服务中。 运行使用分析器帮助诊断可能的缓慢请求后,我们惊讶地发现:

CLRThreadPoolQueue 中缓慢请求的百分比异常高。我们现在 运行 多个配置文件会话返回,每个会话在 CLRThreadPoolQueue 中有 40-80%(这是我们以前在以前的配置文件中从未见过的)。 CPU 每次都低于 40%,并且在检查我们的指标后,我们并没有遇到突然的请求激增。

大多数列为缓慢的请求都是超级简单的 api 调用。我们添加了响应缓存并使它们异步。他们所做的唯一一件事就是访问数据库以查找单个记录结果。我们检查了数据库上的指标,查询平均 运行 时间约为 50 毫秒或更短。查看这些请求的应用程序洞察证实了这一点,并表明数据库查询直到请求时间线的最后才发生(我假设这是队列中的请求)。

最近我们开始将 SignalR 纳入我们应用程序的一部分。它没有完全使用,但它在代码库中。我们后来改用 Azure SignalR 服务,没有发现任何变化。 SignalR 的添加是我们在遇到此问题后所做的唯一 "major" change/addition。

我知道我们可以扩大 and/or 增加 minWorkerThreads。然而,这感觉就像我只是在治疗症状而不是病因。

我们尝试过的事情:

-- None 这些步骤解决了我们的问题 --

我们如何确定哪些请求 and/or 代码导致请求堆积在 CLRThreadPoolQueue 中?

我们遇到了类似的问题,我猜 SignalR 内部肯定用完了很多线程或其他一些争用的资源。

我们做了三件大有帮助的事情:

  1. 在应用程序启动时调用 ThreadPool.SetMinThreads(400, 1) 以确保线程池有足够的线程来处理从启动开始的所有传入请求

  2. 使用部署的相同代码创建第二个应用服务。在 javascript 中,将 SignalR URL 设置为指向第二个实例。这样,所有 SignalR 请求都转到一个应用程序服务,而应用程序的所有 HTTP 请求都转到另一个。显然这需要设置 SignalR 背板,但假设您的应用服务有超过 1 个实例,您无论如何都必须这样做

  3. 查看任何同步代码路径的代码(例如,对数据库或 API 进行非异步调用)并将它们转换为异步代码路径