최근 진행된 SCOM/SSRS 관련 Troubleshooting에서 처음으로 64bit Windows 2008에서 작동 중인 IIS 프로세스 디버깅을 시도 해 봤습니다.
물론 Managed ASP.Net 서비스 인 관계로 Psscor2 64bit 버젼으로 분석은 가능 했습니다만 초기에 덤프 수집이 쉽지 않더군요.
우선 문제는
1. DebugDiag를 설치하여 Hang Dump를 시도 하였으나 실패 오류 메시지와 함께 덤프가 생성되지 않았습니다.
2. WinDBG를 이용한 “cscript adplus.vbs –iis –hang”명령이 작동하지 않습니다. 오류 메시지가 뜨네요. 내용은 –iis가 테스트 된 적이 없기 때문에 지원을 하지 않는다는 내용 이었습니다.
할 수 없이 cscript adplus.vbs –hang –pn w3wp.exe 를 통해서 Hang Dump를 시도했고 이부분은 문제가 없이 진행이 가능 했습니다.
Windows 2008, IIS 7.0의 지원시에 Crash 및 Hang dump의 수집을 위해서는 WinDBG와 Process Name 또는 Process ID를 확인하고 덤프를 수집하는 것이 좋을 것으로 보입니다. 물론 좀 있으면 바뀌겠지만요.
또다른 문제로 관련 케이스의 문제 사항이 IIS ApplicationPool의 Non-responsive 상태 였습니다. 즉, IE에서 관련 URL을 열면 모든 Request에 대해서 대기 상태에 빠진다는 것 이었습니다.
특이한 것은 모든 작업 중인 Thread들이 아래와 같은 Stack Trace를 보여 준다는 것이었습니다.
Child-SP RetAddr Call Site
0000000005a3d830 00000642749e9170 DomainNeutralILStubClass.IL_STUB(IntPtr, Byte*, Int32, System.Net.Sockets.SocketFlags)
0000000005a3d910 00000642749e9234 System.Net.Sockets.Socket.Receive(Byte[], Int32, Int32, System.Net.Sockets.SocketFlags, System.Net.Sockets.SocketError ByRef)
0000000005a3d9a0 0000064274a17eaa System.Net.Sockets.Socket.Receive(Byte[], Int32, Int32, System.Net.Sockets.SocketFlags)
0000000005a3d9f0 0000064274a4c884 System.Net.Sockets.NetworkStream.Read(Byte[], Int32, Int32)
0000000005a3da80 0000064274a487c3 System.Net.PooledStream.Read(Byte[], Int32, Int32)
0000000005a3dab0 0000064274a7df75 System.Net.Connection.SyncRead(System.Net.HttpWebRequest, Boolean, Boolean)
0000000005a3db50 0000064274a7dfb5 System.Net.HttpWebRequest.EndWriteHeaders(Boolean)
0000000005a3dbb0 0000064274a41072 System.Net.HttpWebRequest.WriteHeadersCallback(System.Net.WebExceptionStatus, System.Net.ConnectStream, Boolean)
0000000005a3dbe0 0000064274a7e1ae System.Net.ConnectStream.WriteHeaders(Boolean)
0000000005a3dc80 0000064274a4a275 System.Net.HttpWebRequest.EndSubmitRequest()
0000000005a3dcd0 0000064274a4ae1a System.Net.Connection.CompleteStartRequest(Boolean, System.Net.HttpWebRequest, System.Net.TriState)
0000000005a3dd30 0000064274a71574 System.Net.Connection.SubmitRequest(System.Net.HttpWebRequest)
0000000005a3ddc0 0000064274a7bc9d System.Net.ServicePoint.SubmitRequest(System.Net.HttpWebRequest, System.String)
0000000005a3de30 0000064274a7f951 System.Net.HttpWebRequest.SubmitRequest(System.Net.ServicePoint)
0000000005a3de90 000006424ba875ba System.Net.HttpWebRequest.GetResponse()
0000000005a3df20 000006424ba87640 System.Web.Services.Protocols.WebClientProtocol.GetWebResponse(System.Net.WebRequest)
0000000005a3dfb0 0000064280d22f41 System.Web.Services.Protocols.HttpWebClientProtocol.GetWebResponse(System.Net.WebRequest)
0000000005a3dfe0 000006424ba78803 Microsoft.ReportingServices.UI.Global+RSWebServiceWrapper.GetWebResponse(System.Net.WebRequest)
0000000005a3e020 0000064280d21f25 System.Web.Services.Protocols.SoapHttpClientProtocol.Invoke(System.String, System.Object[])
0000000005a3e0d0 0000064280d21cb4 Microsoft.SqlServer.ReportingServices2005.ReportingService2005.ListSecureMethods()
0000000005a3e110 0000064280d21aa4 Microsoft.SqlServer.ReportingServices2005.RSConnection.GetSecureMethods()
0000000005a3e180 0000064280d2199d Microsoft.ReportingServices.UI.Global+RSWebServiceWrapper.GetSecureMethods()
0000000005a3e1f0 0000064280d21827 Microsoft.SqlServer.ReportingServices2005.RSConnection.IsSecureMethod(System.String)
0000000005a3e240 0000064280d12334 Microsoft.SqlServer.ReportingServices2005.RSConnection.ValidateConnection()
0000000005a3e2a0 0000064280d12235 Microsoft.ReportingServices.UI.Global.SecureAllAPI()
0000000005a3e2d0 0000064280d11a33 Microsoft.ReportingServices.UI.ReportingPage.EnsureHttpsLevel(Microsoft.ReportingServices.Diagnostics.HttpsLevel)
0000000005a3e310 000006428000a119 Microsoft.ReportingServices.UI.ReportingPage.ReportingPage_Init(System.Object, System.EventArgs)
0000000005a3e430 00000642bc901d50 System.Web.UI.Control.OnInit(System.EventArgs)
0000000005a3e470 00000642bc8d8a88 System.Web.UI.Page.OnInit(System.EventArgs)
0000000005a3e4a0 00000642bc8ffb62 System.Web.UI.Control.InitRecursive(System.Web.UI.Control)
0000000005a3e500 00000642bc900a5b System.Web.UI.Page.ProcessRequestMain(Boolean, Boolean)
0000000005a3e5d0 00000642bc90111b System.Web.UI.Page.ProcessRequest(Boolean, Boolean)
0000000005a3e630 00000642bc90190f System.Web.UI.Page.ProcessRequest()
0000000005a3e690 0000064280d11900 System.Web.UI.Page.ProcessRequest(System.Web.HttpContext)
0000000005a3e6f0 00000642bcb2447e ASP.pages_folder_aspx.ProcessRequest(System.Web.HttpContext)
0000000005a3e720 00000642bcb27ba1 System.Web.HttpApplication+CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
0000000005a3e7d0 00000642bcb237b5 System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef)
0000000005a3e870 00000642bcb29d36 System.Web.HttpApplication+ApplicationStepManager.ResumeSteps(System.Exception)
0000000005a3e920 00000642bcafbdfa System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(System.Web.HttpContext, System.AsyncCallback, System.Object)
0000000005a3e980 00000642bc92c721 System.Web.HttpRuntime.ProcessRequestInternal(System.Web.HttpWorkerRequest)
0000000005a3ea10 00000642bc9a4e30 System.Web.HttpRuntime.ProcessRequestNoDemand(System.Web.HttpWorkerRequest)
0000000005a3ea50 000006427f67ba32 System.Web.Hosting.ISAPIRuntime.ProcessRequest(IntPtr, Int32)
0:028> !do 000000017fcfb278
Name: System.String
MethodTable: 00000642788c1a90
EEClass: 00000642788c1998
Size: 130(0x82) bytes
GC Generation: 1
(C:\Windows\assembly\GAC_64\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: http://localhost/ReportServer/ReportService2005.asmx
Fields:
MT Field Offset Type VT Attr Value Name
00000642788ca730 4000096 8 System.Int32 1 instance 53 m_arrayLength
00000642788ca730 4000097 c System.Int32 1 instance 52 m_stringLength
00000642788c4fa8 4000098 10 System.Char 1 instance 68 m_firstChar
00000642788c1a90 4000099 20 System.String 0 shared static Empty
>> Domain:Value 0000000001f27770:0000064278878f28 0000000001fa75a0:0000064278878f28 00000000081b0690:0000064278878f28 0000000001fbccb0:0000064278878f28 <<
000006427890a8e8 400009a 28 System.Char[] 0 shared static WhitespaceChars
>> Domain:Value 0000000001f27770:000000015fbf0748 0000000001fa75a0:00000001bfbf09d0 00000000081b0690:00000000ffd35bc8 0000000001fbccb0:000000017fd0eea8 <<
자, 그럼 왜 이 W3WP.EXE는 Request 를 처리하지 못하고 계속 Waiting 상태에 빠졌을까요.
이유는 이렇습니다. ASP.Net 2.0의 경우 1.1과 달리 대부분의 경우 KB821268의 설정을 자동 적으로 설정해 주게되어 있습니다.
아래에 붉게 표시된 줄을 유심히 보시기 바랍니다.
· Set the values of the maxWorkerThreads parameter and the maxIoThreads parameter to 100.
· Set the value of the maxconnection parameter to 12*N (where N is the number of CPUs that you have).
· Set the values of the minFreeThreads parameter to 88*N and the minLocalRequestFreeThreads parameter to76*N.
· Set the value of minWorkerThreads to 50. Remember, minWorkerThreads is not in the configuration file by default. You must add it.
문제 시점에 수집된 덤프에서 아래와 같이 위의 수식을 잘 적용한 내용이 보이네요.
0:000> vertarget
Windows Server 2008/Windows Vista SP1 Version 6001 (Service Pack 1) MP (8 procs) Free x64
Product: LanManNt, suite: Enterprise TerminalServer SingleUserTS
kernel32.dll version: 6.0.6001.18000 (longhorn_rtm.080118-1840)
Machine Name:
Debug session time: Mon Dec 8 17:22:44.000 2008 (GMT+9)
System Uptime: 0 days 2:25:21.165
Process Uptime: 0 days 2:22:45.000
Kernel time: 0 days 0:00:02.000
User time: 0 days 0:00:52.000
0:000> !do 0x00000000ffbf8188
Name: System.Web.RequestQueue
MethodTable: 00000642bcf65d18
EEClass: 00000642bcf65c58
Size: 88(0x58) bytes
GC Generation: 2
(C:\Windows\assembly\GAC_64\System.Web\2.0.0.0__b03f5f7f11d50a3a\System.Web.dll)
00000642788ca730 4001203 28 System.Int32 1 instance 704 _minExternFreeThreads
00000642788ca730 4001204 2c System.Int32 1 instance 608 _minLocalFreeThreads
00000642788ca730 4001205 30 System.Int32 1 instance 5000 _queueLimit
00000642788e7c68 4001206 40 System.TimeSpan 1 instance 00000000ffbf81c8 _clientConnectedTime
00000642788dca58 4001207 3c System.Boolean 1 instance 1 _iis6
00000642788d5080 4001208 8 ...Collections.Queue 0 instance 00000000ffbf81e0 _localQueue
00000642788d5080 4001209 10 ...Collections.Queue 0 instance 00000000ffbf8338 _externQueue
00000642788ca730 400120a 34 System.Int32 1 instance 25 _count
00000642788f6358 400120b 18 ...ding.WaitCallback 0 instance 00000000ffbf8490 _workItemCallback
00000642788ca730 400120c 38 System.Int32 1 instance 0 _workItemCount
00000642788dca58 400120d 3d System.Boolean 1 instance 0 _draining
00000642788e7c68 400120e 48 System.TimeSpan 1 instance 00000000ffbf81d0 _timerPeriod
00000642788ec7e8 400120f 20 ...m.Threading.Timer 0 instance 00000000ffbf8510 _timer
아래 블로그의 포스팅 내용을 보시면 다음과 같은 내용이 있습니다.
ANSWER: POP QUIZ: What are Free Threads in the Threadpool
http://blogs.msdn.com/tom/archive/2008/07/22/answer-pop-quiz-what-are-free-threads-in-the-threadpool.aspx
So if you have maxWorkerThreads set to 100 on a single processor machine and minFreeThreads set to 88, that means 12 threads can run aspx requests at the same time and 88 threads are available for web service calls, timers, etc.
아래의 ThreadPool의 덤프 내용과 비교해 보면 400 (아마도 50 * CPU) – 704 = -304이 나오게 되죠. 즉, ASPX 페이지를 처리할 수 있는 Thread가 없다는 것 입니다. 그래서 Service Hang이 발생한다는 것 입니다. 아마도 프로세스 갯수가 4개이하 였다면 생기지 않았을 가능성이 높습니다.
0:000> !ThreadPool
Work Request in Queue: 0
--------------------------------------
Number of Timers: 28
CPU utilization 1%
Worker Thread: Total: 9 Running: 8 Idle: 1 MaxLimit: 400 MinLimit: 4
Completion Port Thread:Total: 1 Free: 1 MaxFree: 16 CurrentLimit: 0 MaxLimit: 400 MinLimit: 4
이 문제를 해결하기 위해서는 과감하게 .Net Framework 2.0의 Auto 설정을 깨고 메뉴얼로 설정을 바꿔야 합니다.
일반적으로 C:\Windows\Microsoft.NET\Framework64\v2.0.50727\CONFIG 폴더 아래에 있는 machine.config 와 web.config의 설정을 아래와 같이 변경해 주시면 됩니다.
- Machine.config 에서
<processModel autoConfig="true"/> 를 찾으신 후 아래의 라인으로 변경 합니다.
<processModel maxWorkerThreads="100" maxIoThreads="100" minWorkerThreads="50"/>
- 같은 폴더 아래의 web.config 파일을 여신 후 아래의 내용을 <system.web> configuration section 아래에 추가 합니다.
<httpRuntime minFreeThreads="352" minLocalRequestFreeThreads="304" appRequestQueueLimit="5000"/>
아래의 URL들을 참고하세요.
Chapter 17 — Tuning .NET Application Performance
http://msdn.microsoft.com/en-us/library/ms998583.aspx
Contention, poor performance, and deadlocks when you make Web service requests from ASP.NET applications
http://support.microsoft.com/kb/821268/en-us
Support WebCast: Microsoft ASP.NET Threading
http://support.microsoft.com/default.aspx?scid=/servicedesks/webcasts/en/transcripts/wct060503.asp
http://support.microsoft.com/default.aspx?scid=kb;en-us;820913
ASP.NET Thread Usage on IIS 7.0 and 6.0
http://blogs.msdn.com/tmarq/archive/2007/07/21/asp-net-thread-usage-on-iis-7-0-and-6-0.aspx