问题描述:
某二次开发的项目反馈,不定期出现应用服务器无响应的情况,登录服务器发现任务管理器中有大量的w3wp僵尸进程。
分析过程:
针对同一进程每隔15秒抓取dump,连续抓取3个,对比发现线程信息没有变化,并且每个线程的CPU时间和堆栈都没有变化,奇怪???
以其中一个僵尸进程的dump日志为例,总计61个工作线程,其中正在运行的为15个,但我们仅能查看其中3个线程的信息。
0:000> .load d:\dumps\sos.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 = ; !asyResult.IsCompleted && i <= ; i += )
{
Thread.Sleep();
}
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请求超时");
}