问题描述:
某二次开发的项目反馈,不定期出现应用服务器无响应的情况,登录服务器发现任务管理器中有大量的w3wp僵尸进程。
分析过程:
针对同一进程每隔15秒抓取dump,连续抓取3个,对比发现线程信息没有变化,并且每个线程的CPU时间和堆栈都没有变化,奇怪???
以其中一个僵尸进程的dump日志为例,总计61个工作线程,其中正在运行的为15个,但我们仅能查看其中3个线程的信息。
0:000> .load d:dumpssos.dll 0:000> !tp CPU utilization: 3% Worker Thread: Total: 61 Running: 15 Idle: 46 MaxLimit: 5600 MinLimit: 56 Work Request in Queue: 0 -------------------------------------- Number of Timers: 263 -------------------------------------- Completion Port Thread:Total: 1 Free: 1 MaxFree: 112 CurrentLimit: 0 MaxLimit: 5600 MinLimit: 56 0:000> !runaway User Mode Time Thread Time 1:259e4 0 days 0:02:57.107 2:1fa68 0 days 0:01:45.706 0:1bc54 0 days 0:00:49.561
进一步对比这三个可见线程的堆栈显示,都在等待异构系统Webservice的网络返回。调用异构系统的具体实现为同步的HttpSoap方式,经过ESB调用第三方的认证接口。。。,按照常理来说,该WebService服务应该很简单,什么原因呢?不超时吗?
根据堆栈我们能够看到SoapHttpClientProtocol ---》HttpWebClientProtocol ---》WebClientProtocol ---》HttpWebRequest 依次的调用关系,最终的实现是HttpWebRequest。
按照msdn上官方的文档显示,HttpWebRequest 类有三个关于Timeout的设置:Timeout、ContinueTimeout、ReadWriteTimeout
https://msdn.microsoft.com/zh-cn/library/system.net.httpwebrequest(v=vs.110).aspx
根据进程现象和活动线程的堆栈信息,推测是“远程异构系统”先响应返回HttpHeader信息后,使得Timeout超时属性失效; 之后每5分钟内都有flush回一些信息,ReadWriteTimeout也会失效;当应用程序池在设定的空闲超时限内没有接到新的request请求,触发应用程序池的回收事件;但关闭进程时发现当前进程仍有未处理完的工作进程,当前进程在等待所有工作线程执行完毕然后再退出。当应用收到新的web请求时,应用程序池创建新的w3wp进程接收处理,依次推理,长时间后便出现了很多的僵尸进程。。。。
为什么“第三方系统能够持续的反馈数据,却没有执行完成呢”? 这基本上也是不可能的,个人感觉应该是中间网络设备(防火墙、路由器、交换机)或ESB出现问题导致的,但是如果正向的联调测试话,难度太大,需要协调很多不同的外协单位。。。。
解决办法:
将同步的WebService调用改为异步调用,对超过指定的时限仍未获得返回结果的请求,提示响应超时并对异步请求做Abort处理。
核心的示例代码如下:
TestSoapHttpClient webClient = new TestSoapHttpClient(); Console.WriteLine("testing webservice: " + webClient.HelloWorld1()); // 同步调用示例 object asyState = new object(); var asyResult = webClient.BeginHelloWorld2((x) => { }, asyState); // 设置超时时限,超时后主动终止web请求 for (int i = 0; !asyResult.IsCompleted && i <= 5000; i += 50) { Thread.Sleep(50); } if (asyResult.IsCompleted) { string result = webClient.EndHelloWorld2(asyResult); // 根据远程Webservice返回的结果,继续业务处理 Console.WriteLine("request succeed: " + result); } else { webClient.Abort(); webClient.Dispose(); // 记录必要的日志,根据情况决定是否抛出异常 Console.WriteLine("web request is aborted after timeout"); // log ...... // throw new Exception("远程Webservice请求超时"); }