当ASP.NET接收到请求时,它如何确定是为其提供服务还是将其排队?我之所以这么问,是因为我正在监视服务器上的性能计数器,CPU并没有达到最大值,而且有大量可用的工作线程,但我仍然看到多达200个请求排队。
我一直在做研究,我相信我已经找到了一个可以接受的答案。我的主要来源是这篇文章:http://blogs.msdn.com/b/tmarq/archive/2007/07/21/asp-net-thread-usage-on-iis-7-0-and-6-0.aspx
据我所知,请求处理主要有两种方式。第一个是MaxConcurrentRequestsPerCPU属性。在.NET 4之前,默认情况下将其设置为12。在.NET4中,它被更改为5000。对于异步请求,他们希望允许很多,对于同步请求,他们相信ASP.NET线程池会很好地抑制同步请求。第二个当然是ThreadPool本身。ASP.NET在那里发布请求后,它可以决定何时发送请求。
如果您正在进行异步处理,那么限制因素可能是CPU、网络和磁盘,而不是任何ASP.NET请求限制。它可能会达到MaxConcurrentRequestsPerCPU的限制,但这个限制确实很高。
如果你长时间对网络调用进行同步处理和阻塞,你很可能会遇到这些限制。MaxConcurrentRequestsPerCPU在.NET 4之前是需要注意的,但仍然存在线程池。
性能测试
我做了一个简单的测试,看看这种节流是如何工作的。我有一个500毫秒线程.Sleep()调用的简单页面。一台主机同时发出800个异步请求,一台运行ASP.NET的工作机处理所有请求。结果很有趣:
.NET 3.5,无需修改:46秒。使用process explorer锯出9个工作线程
.NET 3.5,MaxConcurrentRequestsPerCPU设置为5000:46秒。9个工作线程
.NET 4:42秒,或者在热运行时为13秒。锯大约35条工人线逐渐形成
.NET 4,异步:3秒
一些观察:
- MaxConcurrentRequestsPerCPU未被命中。看起来这是ThreadPool本身的限制
- .NET 3.5似乎不太愿意创建新的线程来处理同步请求。NET 4在处理负载方面做得更好
- 异步仍然是最好的国家英里
IIS在开始排队请求之前不会使用所有可用线程,因为如果正在执行的请求需要额外的线程,则这些线程需要保持可用。IIS经过优化,优先考虑执行请求,并且它不希望执行请求因工作进程用完可用线程而被阻止。
默认的最大线程池20,最小空闲为8,这意味着在新请求排队之前,系统将只执行12个请求。最大线程数乘以内核数,但最小线程数不是,因此默认情况下,在双核盒排队之前,它将允许32个请求。
至于剩余的CPU,ASP.NET不会对此进行监控。它纯粹是关于正在使用的线程数。这些线程可能会被磁盘访问、传输DB结果的网络访问或简单的Thread.Sleep阻塞,即使CPU没有达到最大值,这些都会导致新的请求进入队列。
更多信息可在MS Patterns&绩效实践手册。它适用于IIS6/.NET 1.1,但这些概念仍然存在。http://msdn.microsoft.com/en-us/library/ff647787.aspx#scalenetchapt06_topic8
为IIS7/.NET 2+重新配置:http://msdn.microsoft.com/en-us/library/e1f13641.aspx