请求在Azure AppService中排队,尽管它在线程池中有足够的线程

我使用asp.net webapi编写了一个api,并将其部署在azure中作为Appservice。 我的控制器的名称是TestController,我的动作方法类似于波纹管。

[Route("Test/Get")] public string Get() { Thread.Sleep(10000); return "value"; } 

因此,对于每个请求,它应该在返回字符串“value”之前等待10秒。 我还编写了另一个端点来查看threadpool中用于执行请求的线程数。 这个动作就像吼叫一样。

  [Route("Test/ThreadInfo")] public ThreadPoolInfo Get() { int availableWorker, availableIO; int maxWorker, maxIO; ThreadPool.GetAvailableThreads(out availableWorker, out availableIO); ThreadPool.GetMaxThreads(out maxWorker, out maxIO); return new ThreadPoolInfo { AvailableWorkerThreads = availableWorker, MaxWorkerThreads = maxWorker, OccupiedThreads = maxWorker - availableWorker }; } 

现在,当我们同时向Test / Get端点发出29次调用时,所有请求都需要大约11秒才能成功。 因此服务器在11个线程中同时执行所有请求。 要查看线程状态,在调用Test / Get后立即调用Test / ThreadInfo会立即返回(无需等待){“AvailableWorkerThreads”:8161,“MaxWorkerThreads”:8191,“OccupiedThreads”:30}

似乎29个线程正在执行Test / Get请求,1个线程正在执行Test / ThreadInfo请求。

当我接到60次调用Test / Get时,需要将近36秒才能获得成功。 调用Test / ThreadInfo(需要一些时间)返回{“AvailableWorkerThreads”:8161,“MaxWorkerThreads”:8191,“OccupiedThreads”:30}

如果我们增加请求数,则OccupiedThreads的值会增加。 与1000次请求一样,它需要2分22秒,而OccupiedThreads的值为129。

虽然很multithreading在WorkerThread中可用,但似乎请求并在30个并发调用之后排队。 逐渐增加并发执行的线程,但这还不够(1000请求为129)。

由于我们的服务有很多IO调用(其中一些是外部api调用,一些是数据库查询),因此延迟也很高。 由于我们使用所有IO调用异步方式,因此服务器可以同时处理大量请求,但是当处理器正在进行实际工作时我们需要更多的并发性。 我们正在使用S2服务计划和一个实例。 增加实例会增加并发性,但我们需要从单个实例获得更多的并发性。

在阅读了一些关于IIS的博客和文档后,我们看到有一个设置minFreeThreads 。 如果线程池中的可用线程数低于此设置的值,则IIS开始对请求进行排队。 appservice有这样的东西吗? 是否真的有可能从azure app服务获得更多的并发性,或者我们在那里缺少一些配置?

最后得到了我的问题的答案。 问题是ASP.NET线程池维护了一个线程池,这些线程已经引起了线程初始化成本并且易于重用。 .NET线程池也是自我调整的。 它监视CPU和其他资源利用率,并根据需要添加新线程或修剪线程池大小。 当有大量请求且池中没有足够的线程可用时,线程池开始在池中添加新线程,在此之前它运行自己的算法以查看内存状态和CPU使用系统需要长时间的时间,这就是为什么我们看到池中的工作线程缓慢增加并且大量请求排队。 但幸运的是,在线程池切换到添加新线程的算法之前,可以选择设置工作线程的数量。 代码就像波纹管。

  public string Settings() { int minWorker, minIOC; ThreadPool.GetMinThreads(out minWorker, out minIOC); if (ThreadPool.SetMinThreads(300, minIOC)){ return "The minimum number of threads was set successfully."; } else { return "The minimum number of threads was not changed."; } } 

这里ThreadPool.SetMinThreads(300,minIOC)设置了线程池在切换到添加或删除线程的算法之前将创建的最小线程的值。 我添加了此方法作为我的webapi控制器的操作,然后在我向Test / Get端点发出300个并发请求时发出请求后运行此操作,所有请求都在11秒内运行并完成,并且没有请求排队。

根据我的理解,您需要通过ThreadPool.GetMinThreads检查MinWorkerThreads ,它可以检索ThreadPool在预期新请求时维护的空闲线程数。 我建议您在执行操作( Tetst/Get )后返回当前线程池信息,而不是向Test/ThreadInfo发出新请求。 根据您的代码,我在我的网络应用程序上使用B1定价层对其进行了测试,如下所示:

1000个并发请求,每个动作睡眠15秒,总体时间为3分20秒。

在此处输入图像描述

线程池创建并销毁工作线程以优化吞吐量,如果当前线程可以处理请求,则不会再创建线程。 一旦这些线程完成执行其活动,它们就会返回到线程池。

然后,我使用异步编程并更改了如下操作:

 public async Task DoJob(int id) { await Task.Delay(TimeSpan.FromSeconds(15)); return ThreadInfo(); } 

结果:

在此处输入图像描述

近3000个并发请求:

在此处输入图像描述

通常, AvailableWorkerThreads只表示可以启动的其他工作线程数,而不是为您创建的工作线程数。 我建议您使用异步编程并在azure Web应用程序上部署您的实际工作,然后检查实际性能并找出相关方法(如果有任何瓶颈)。