Tob*_*999 29 asp.net iis queue asynchronous
下面有一点长的描述,但这是一个非常棘手的问题.我试图覆盖我们对该问题的了解,以缩小搜索范围.问题更多的是正在进行的调查,而不是基于单一问题的调查,但我认为它也可能有助于其他人.但如果您认为我对下面的某些假设有误,请在评论中添加信息或纠正我.
更新2013年2月19日:我们已经清除了一些问号,我有一个理论,我将在下面更新主要问题.但是还没准备好写一个"已解决"的回复.
2013年4月24日更新:现在生产稳定(虽然我认为这是暂时的),但我认为这是由于两个原因造成的.1)端口增加,2)减少传出(转发)请求的数量.我会在正确的背景下继续这个更新.
我们目前正在我们的生产环境中进行调查,以确定当进行太多传出异步Web服务请求时,我们的IIS Web服务器无法扩展(一个传入请求可能会触发多个传出请求).
CPU只有20%,但我们收到传入请求的HTTP 503错误,许多传出的Web请求得到以下异常:"SocketException:无法执行套接字上的操作,因为系统缺少足够的缓冲区空间或因为队列是"显然,某个地方存在可扩展性瓶颈,我们需要找出它是什么以及是否有可能通过配置来解决它.
应用背景:
我们在Windows 2008 R2 64位操作系统上使用.NET 4.5运行IIS v7.5集成托管管道.我们在IIS中只使用了一个工作进程.硬件略有不同,但用于检查错误的机器是Intel Xeon 8核心(16个超线程).
我们使用异步和同步Web请求.那些异步的是使用新的.NET异步支持来使每个传入请求在应用程序中向持久化TCP连接上的其他服务器发出多个HTTP请求(保持活动状态).同步请求执行时间低0-32 ms(由于线程上下文切换而发生更长时间).对于异步请求,在请求中止之前,执行时间最长可达120毫秒.
通常,每台服务器最多可以提供约1000个传入请求.当问题开始出现时,传出请求是~300个请求/秒,最多约600个请求/秒.仅在传出异步时才会出现问题.请求在服务器上启用,我们超过一定级别的传出请求(~600 req./s).
解决问题的可能方法:
在这个问题上搜索互联网揭示了许多可能的候选解决方案.但是,它们非常依赖于.NET,IIS和操作系统的版本,所以在我们的上下文中找到一些东西需要时间(anno 2013).
下面列出了解决方案候选人以及我们迄今为止在配置背景方面得出的结论.到目前为止,我已将检测到的问题区域分类为以下主要类别:
传出的异步请求异常消息确实指示某个缓冲区队列已被填满.但它没有说哪个队列/缓冲区.通过IIS论坛(以及那里引用的博客文章),我已经能够在下面标记为AF的请求管道中区分4个可能的6个(或更多)不同类型的队列.
虽然应该说明所有下面定义的队列,我们可以肯定地看到1.B)ThreadPool性能计数器Requests Queued在有问题的负载期间变得非常满.因此问题的原因可能是.NET级别而不是低于此级别(CF).
我们使用.NET框架类WebClient发出异步调用(异步支持),而不是我们遇到的HttpClient具有相同的问题,但具有低得多的req/s阈值.我们不知道.NET Framework实现是否隐藏任何内部队列或不在线程池之上.我们认为情况并非如此.
线程池充当自然队列,因为.NET线程(默认)调度程序正从线程池中挑选线程以执行.
性能计数器:[ASP.NET v4.0.30319].[请求排队].
配置可能性:
如果线程池已满,请求将开始堆积在此本机(未管理)队列中.
性能计数器: [ASP.NET v4.0.30319].[本机队列中的请求]
配置可能性: ????
此队列与上面的1.C)队列不同.这是我所说的解释"HTTP.sys内核队列本质上是一个完成端口,用户模式(IIS)从内核模式(HTTP.sys)接收请求.它有一个队列限制,当超过该限制时,您将收到503状态代码.HTTPErr日志还将通过记录503状态和QueueFull来指示这发生了.
性能计数器:我无法为此队列找到任何性能计数器,但是通过启用IIS HTTPErr日志,应该可以检测此队列是否被泛洪.
配置可能性:这是在应用程序池的IIS中设置的,高级设置:队列长度.默认值为1000.我已经看到建议将其增加到10.000.虽然尝试这种增加并没有解决我们的问题.
虽然不太可能,但我猜操作系统实际上可能在网卡缓冲区和HTTP.sys队列之间的某处有一个队列.
当请求到达网卡时,将它们放置在某个缓冲区中以便被某些OS内核线程拾取是很自然的.由于这是内核级执行,因此速度很快,因此它不太可能是罪魁祸首.
Windows性能计数器:使用网卡实例[网络接口].[已收到丢弃的数据包].
配置可能性: ????
虽然我们的传出(异步)TCP请求是由持久(保持活动)的TCP连接组成的,但这是在这里和那里弹出的候选者.因此,随着流量的增长,可用的临时端口数量实际上只会因传入的请求而增长.我们确信只有在启用传出请求时才会出现问题.
但是,由于在请求的较长时间范围内分配端口,可能仍会出现问题.传出请求可能需要120毫秒才能执行(在.NET任务(线程)被取消之前),这可能意味着端口数量被分配更长的时间段.分析Windows性能计数器,验证这一假设,因为TCPv4的数量.[连接建立]在问题发生时从正常的2-3000变为峰值,总计接近12.000.
我们已经验证配置的最大TCP连接数设置为默认值16384.在这种情况下,它可能不是问题,尽管我们危险地接近最大限制.
当我们尝试在服务器上使用netstat时,它根本不返回任何输出,也使用TcpView在开头显示很少的项目.如果我们让TcpView运行一段时间,它很快就会很快开始显示新的(传入)连接(比如25个连接/秒).几乎所有连接从一开始就处于TIME_WAIT状态,表明它们已经完成并等待清理.那些连接是否使用临时端口?本地端口始终为80,远程端口正在增加.我们想使用TcpView来查看传出连接,但我们根本看不到它们,这很奇怪.这两个工具不能处理我们的连接数量吗? (待续......但如果你知道的话请填写信息...)
更多的是,作为一个侧面踢这里.在此博客文章" IIS 7.5,IIS 7.0和IIS 6.0上的ASP.NET线程使用 "中建议将ServicePointManager.DefaultConnectionLimit设置为int maxValue,否则可能会出现问题.但是在.NET 4.5中,这从一开始就是默认值.
2013年2月19日更新:
结束更新2013年2月19日
更新2013年4月24日: 我们已将端口数量增加到最大值.与此同时,我们没有获得与之前一样多的转发传出请求.这两个组合应该是我们没有发生任何事故的原因.但是,这只是暂时的,因为将来这些服务器上的传出请求数量必然会再次增加.因此,问题在于,传入请求的端口必须在转发请求响应的时间范围内保持打开状态.在我们的应用程序中,这些转发请求的取消限制为120毫秒,可以与正常<1毫秒进行比较,以处理未转发的请求.所以从本质上讲,我认为确定数量的端口是我们正在使用的高吞吐量服务器(在大约16个核心机器上大于1000个请求/秒)的主要可扩展性瓶颈.这与GC对缓存重新加载的工作(见下文)相结合,使服务器特别具有可用性.
最后更新24/4
我们的性能计数器显示线程池(1B)中排队请求的数量在问题发生时波动很大.因此,这意味着我们有一种动态情况,其中队列长度由于环境的变化而开始振荡.例如,如果存在在流量泛滥时激活的洪泛保护机制,则会出现这种情况.事实上,我们有许多这样的机制:
当事情变得非常糟糕并且服务器响应HTTP 503错误时,负载均衡器将自动删除Web服务器在生产中活动15秒.这意味着其他服务器将在时间范围内承担增加的负载.在"冷却期"期间,服务器可以完成其请求的服务,并且当负载均衡器进行下一次ping时它将自动恢复.当然,只要所有服务器一次都没有问题,这只会很好.幸运的是,到目前为止,我们还没有遇到过这种情况.
在Web应用程序中,我们有自己构造的阀门(是.它是一个"阀门".不是"值")由线程池中的排队请求的Windows性能计数器触发.在Application_Start中有一个线程,它每秒检查一次性能计数器值.如果该值超过2000,则所有传出流量都将停止启动.下一秒,如果队列值低于2000,则传出流量再次开始.
这里奇怪的是它没有帮助我们达到错误场景,因为我们没有太多记录这种情况.这可能意味着当交通遇到困难时,事情会很快变坏,因此1秒的时间间隔检查实际上太高了.
还有另一个方面.当应用程序池中需要更多线程时,这些线程的分配速度非常慢.从我读到的,每秒1-2个线程.这是因为创建线程的成本很高,并且因为为了避免在同步情况下昂贵的上下文切换,你不需要太多线程,我认为这是很自然的.但是,它也应该意味着如果突然大量的流量突然袭击我们,线程的数量将不足以满足异步场景中的需求并且将开始请求的排队.我认为这是一个非常可能的问题候选人.然后,一个候选解决方案可能是增加ThreadPool中创建的最小线程数量.但我想这可能也会影响同步运行请求的性能.
(Joey Reyes 在博客文章中写到了这一点) 由于以后异步请求收集了对象(在我们的案例中晚了120分钟),因为对象可以被提升到第1代并且内存不会被重新收集,因此可能会出现内存问题经常应该这样.垃圾收集器上的压力增加可能会导致扩展的线程上下文切换发生,并进一步削弱服务器的容量.
但是,我们没有看到在问题出现期间GC和CPU使用率的增加,因此我们认为建议的CPU限制机制不是我们的解决方案.
更新2013年2月19日:我们在常规的intervalls中使用缓存交换机制,在这种机制中,(几乎)完整的内存缓存被重新加载到内存中,旧的缓存可以被垃圾收集.在这些时候,GC必须更加努力,并从正常的请求处理中窃取资源.使用Windows性能计数器进行线程上下文切换时,它表明上下文切换的数量与高GC使用时的正常高值相比显着降低.我认为在这样的缓存重新加载期间,服务器对于排队请求是非常脆弱的,因此有必要减少GC的占用空间.解决问题的一个可能方法就是只填充缓存而不必一直分配内存.多一点工作,但它应该是可行的.
更新2013年4月24日: 我仍然在缓存重新加载内存调整的中间,以避免GC运行尽可能多.但是,当GC运行时,我们通常会暂时有大约1000个排队的请求.由于它在所有线程上运行,因此从正常的请求处理中窃取资源是很自然的.一旦部署了此调整,我将更新此状态,我们可以看到差异.
最后更新24/4
由于异步请求会占用 tcp 套接字更长时间,因此您可能需要查看 web.config 中连接管理中的maxconnection属性?请参考此链接:http://support.microsoft.com/default.aspx ?scid=kb;en-us;821268
我们遇到了类似的问题并调整了此参数来解决我们的问题。也许这会对你有所帮助。
编辑:此外,根据过去的经验,大量 TIME_WAIT 表明代码中存在连接泄漏。可能的原因: 1) 未处理使用过的连接。2)连接池的实现不正确。
| 归档时间: |
|
| 查看次数: |
10699 次 |
| 最近记录: |