一个由Response.Redirect 引起的性能问题的分析

现象:

某系统通过单点登录(SSO) 技术验证用户登录。用户在SSO 系统上通过验证后,跳转到某系统的主页上面。而跳转的时间很长,约1分钟以上。

分析步骤:

在问题复现时抓取Hang dump 进行分析:

1) 找到用户登陆的调用堆栈

0:071> kb

RetAddr           : Args to Child                                                           : Call Site

000007fe`f81889e5 : 00000000`0d379e30 00000000`0d379490 00000000`0d378ba0 00000000`00000000 : MSVCR100_CLR0400!memcpy+0x266

000007fe`f818915c : 00000000`0d378b70 000007fe`f8251a30 00000000`0000252c 000007fe`f398d1c8 : clr!StackFrameIterator::ProcessCurrentFrame+0x56a

000007fe`f818936a : 00000000`00000000 00000000`0d378b70 000007fe`f398d19c 00000000`0d378b70 : clr!StackFrameIterator::NextRaw+0x35c

000007fe`f8188d8c : 00000000`0d379420 00000000`00000002 00000000`00000002 00000000`0d378f40 : clr!StackFrameIterator::Next+0x22

000007fe`f826e2a7 : 00000000`0e0936d0 00000000`0e0936d0 00000000`00000000 00000000`00000000 : clr!Thread::StackWalkFramesEx+0x9e

000007fe`f83350f0 : 00006dae`0a62919f 00000000`0e0936d0 00000000`00000000 00000000`00000000 : clr!Thread::ReadyForAsyncException+0x197

000007fe`f833503b : 00000000`0e0936d0 00000000`00000028 00000000`0e0936d0 000007fe`f87cec00 : clr!Thread::HandleThreadAbort+0x6c

000007fe`f83352d7 : 00000000`00000000 00000000`037bc050 00000000`0e0936d0 000007fe`f87cece0 : clr!CommonTripThread+0x18

000007fe`f81b6615 : 000007fe`f81b6470 00000000`00000000 00000000`00001ad6 00000001`00000028 : clr!FC_GCPoll+0x107

000007fe`f398d1c8 : 00000001`5ff74050 00000000`00001296 00000001`5ff76c08 00000000`00000000 : clr!Buffer::BlockCopy+0x1a5

000007fe`f36b932a : 00006dae`d26d74e7 000007fe`f87cdfc0 00000000`0d37dce8 00000000`00000000 : System_Xml_ni!System.Xml.XmlTextReaderImpl.ShiftBuffer(Int32, Int32, Int32)+0x28

000007fe`f36b8c6d : 00000000`0d37b4d0 00000001`5ff76c08 00000014`00054cfe 000007fe`f718f13e : System_Xml_ni!System.Xml.XmlTextReaderImpl.ParseText(Int32 ByRef, Int32 ByRef, Int32 ByRef)+0x19a

000007fe`f36b8a87 : 00000002`3067ffe0 000007fe`f36c0cdb 000007fe`f37578c0 00000002`30680008 : System_Xml_ni!System.Xml.XmlTextReaderImpl.ParseText()+0x6d

000007fe`f36c02f9 : 00000001`5ff740b0 000007fe`f36bfd6d 00000001`5ff74060 00000001`5ff74050 : System_Xml_ni!System.Xml.XmlTextReaderImpl.ParseElementContent()+0x27

000007fe`f3703caa : 00000001`5ff73a30 00000002`6f9e4320 00000001`5ff74038 00000001`5ff74040 : System_Xml_ni!System.Xml.XmlLoader.LoadNode(Boolean)+0xf9

000007fe`f37036e0 : 00000001`5ff74038 00000001`5ff74040 00000001`5ff738e8 00000000`0d37b670 : System_Xml_ni!System.Xml.XmlLoader.LoadDocSequence(System.Xml.XmlDocument)+0x4a

000007fe`f370308d : 00000001`5ff738e8 00000002`7f992078 00000001`5ff738e8 00000000`0d37b6f0 : System_Xml_ni!System.Xml.XmlDocument.Load(System.Xml.XmlReader)+0x90

000007ff`004007f6 : 00000000`23a3db85 000007fe`f71f1590 00000000`00000000 00000000`00000000 : System_Xml_ni!System.Xml.XmlDocument.Load(System.String)+0xcd

000007ff`0040039c : 00000001`5ff732b0 00000002`6f9e4320 00000001`8f98ee20 80000000`00000001 : XXXXXX_Sql_Common!Common.XmlHelper.InsertNode(System.String, System.String, Boolean, System.String, System.Collections.Hashtable, System.Collections.Hashtable)+0xb6

000007ff`00400060 : 00000002`0f980e00 00000001`5ff72c48 00000001`3f98b328 000007fe`f8266279 : XXXXXX_Sql_Common!Common.ErrorLog.Insert(System.String, System.String, System.String)+0x30c

000007ff`004defbc : 00000002`0f980e00 00000001`5ff72ba8 00000001`3f98b328 00000000`0d37c2c0 : XXXXXX_Sql_Common!Common.ErrorLog.Insert(System.String, System.Exception, System.String)+0x60

000007fe`f82665ed : 00000000`0e0936d0 00000000`0e0936d0 000007ff`004def74 00000000`0d37c370 : XXXXXX_Web!EasyMail.Web.login.Page_Load(System.Object, System.EventArgs)+0x19ec

000007fe`f82663f2 : 00000000`0377e3a0 000007ff`004def74 00000000`0d37dea0 000007ff`004cbe98 : clr!ExceptionTracker::CallHandler+0x1c1

000007fe`f8266342 : 00000000`0d37dea0 00000000`0d37bc80 00000000`0377e3a0 00000000`0d37dea0 : clr!ExceptionTracker::CallCatchHandler+0x7e

00000000`76ec9d8d : 000007ff`004de400 00000000`0d37dea0 00000000`0d37c2c0 00000000`0d37bc80 : clr!ProcessCLRException+0x2d6

00000000`76eb8a2c : 00000000`0d380000 00000000`0d37c2c0 00000206`0005af30 000007fe`f81e9b05 : ntdll!RtlpExecuteHandlerForUnwind+0xd

000007fe`f82686a0 : 00000000`0d37dea0 00000000`0000bad0 00000000`0d37d960 00000000`0e0936d0 : ntdll!RtlUnwindEx+0x539

000007fe`f826865c : 00000000`00000000 00000000`0377e3a0 00000000`0d37c940 00000000`0d37dea0 : clr!ClrUnwindEx+0x40

00000000`76ec9d0d : 000007ff`004de481 00000000`0d37dea0 00000000`0d37d330 00000000`0d37c940 : clr!ProcessCLRException+0x2a0

00000000`76eb91af : 00000000`0d380000 000007fe`cd224604 00000000`0005af30 00000000`036ece50 : ntdll!RtlpExecuteHandlerForException+0xd

00000000`76eb97a8 : 00000000`0d37d960 00000000`0d37d330 00000000`00000001 00000000`00000000 : ntdll!RtlDispatchException+0x45a

000007fe`fd0b9e5d : 00000000`0e0936d0 00000000`00000001 00000000`00000001 00000000`0e0936d0 : ntdll!RtlRaiseException+0x22f

000007fe`f8266d1d : 00000000`0e0936d0 00000000`00000001 00000000`0d37dbf1 00000001`5ff72ba8 : KERNELBASE!RaiseException+0x39

000007fe`f826f91e : 00000000`0d37dbd8 00000001`5ff72ba8 00000000`0e0936d0 00000001`5ff72ba8 : clr!RaiseTheExceptionInternalOnly+0x363

000007fe`f826f76b : 00000000`ffb07948 00000000`00000001 00000000`0e0936d0 00000000`0e0936d0 : clr!Thread::UserAbort+0x19a

000007fe`f71b5929 : 00000002`7f992078 00000001`5ff72b90 00000000`00000000 00000000`00000000 : clr!ThreadNative::Abort+0x14b

000007fe`cd19b9a6 : 00000000`ffb45ad0 00000001`dfe872a8 00000001`8f98edc0 00000000`00000000 : mscorlib_ni!System.Threading.Thread.Abort(System.Object)+0x39

000007ff`004de481 : 00000002`0f980da0 00000001`dfe86a90 00000001`3f98b328 80000000`00000000 : System_Web_ni!System.Web.HttpResponse.End()+0x166

000007fe`fad79c09 : 00000001`ef9f1b18 00000001`ef9f1b18 00000001`3f98add0 00000000`0d37e680 : XXXXXX_Web!XXXXXX.Web.login.Page_Load(System.Object, System.EventArgs)+0xeb1

000007fe`cd1b9217 : 00000001`efa2bfb8 000007fe`cd19e96b 00000000`00000000 00000000`00000000 : System_Web_RegularExpressions_ni!System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr, System.Object, System.Object, System.EventArgs)+0x19

000007fe`cd1b4d38 : 00000001`efa01d90 00000000`ffb68778 00000000`ffb68501 000007fe`cd19ad55 : System_Web_ni!System.Web.UI.Control.LoadRecursive()+0x47

000007fe`cd1b4011 : 00000001`ef9f1b18 000007fe`cd1b6601 00000000`00000001 000007fe`cd1a4f38 : System_Web_ni!System.Web.UI.Page.ProcessRequestMain(Boolean, Boolean)+0xbf8

000007fe`cd1b3ebd : 00000001`ef9f1b18 00000002`7f9980a0 00000000`ffb68501 00000000`00000004 : System_Web_ni!System.Web.UI.Page.ProcessRequest(Boolean, Boolean)+0xc1

000007fe`cd1b2cb7 : 00000001`ef9f1b18 000007fe`cd19fc2a 000007ff`00143cf0 000007fe`cd1bc313 : System_Web_ni!System.Web.UI.Page.ProcessRequest()+0x10d

000007ff`004dc5e3 : 00000000`0d37e850 00000001`efa01d90 00000000`ffb683c0 00000000`0d37e8d0 : System_Web_ni!System.Web.UI.Page.ProcessRequest(System.Web.HttpContext)+0xa7

000007fe`cd1bc7a1 : 00000001`ef9f1b18 00000000`ffb45810 00000001`3fa5bb88 00000000`00000000 : App_Web_0khihdrb!ASP.login_aspx.ProcessRequest(System.Web.HttpContext)+0x33

000007fe`cd1891fe : 00000000`ff9c0001 00000000`ffa11980 00000000`ffb683c0 00000000`00000001 : System_Web_ni!System.Web.HttpApplication+CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()+0x271

000007fe`cd19d49a : 00000001`3fa59cc8 00000001`3fa5bc50 00000000`0d37eab0 000007fe`cd29fd68 : System_Web_ni!System.Web.HttpApplication.ExecuteStep(IExecutionStep, Boolean ByRef)+0x10e

000007fe`cd1886e1 : 00000001`3fa5b9a0 00000000`00000000 00000002`7f9980a0 00000000`0d37ebb0 : System_Web_ni!System.Web.HttpApplication+ApplicationStepManager.ResumeSteps(System.Exception)+0x20a

000007fe`cd18d6ae : 01ce7e07`d83d1609 00000000`ffa6e3b8 00000000`00000000 00000000`00000004 : System_Web_ni!System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(System.Web.HttpContext, System.AsyncCallback, System.Object)+0xf1

000007fe`cd18c377 : 00000000`ff9bf918 00000000`ffb44b30 00000000`0d37ebf0 00000000`00000000 : System_Web_ni!System.Web.HttpRuntime.ProcessRequestInternal(System.Web.HttpWorkerRequest)+0x26e

000007fe`cd90f80d : 00000000`ffa68ed8 00000000`00000000 000007fe`f71ab630 000007fe`f87ce8b0 : System_Web_ni!System.Web.Hosting.ISAPIRuntime.ProcessRequest(IntPtr, Int32)+0x1c7

000007fe`f8249b9e : 00000000`0124fda0 00000000`01338310 00000000`00000002 00000000`0d37f330 : System_Web_ni!DomainNeutralILStubClass.IL_STUB_COMtoCLR(Int64, Int32, Int32 ByRef)+0x1d

000007fe`f8249782 : 00000000`0383a7d0 000007fe`f81e8244 00000000`0d37f0a8 00000000`00000000 : clr!COMToCLRDispatchHelper+0x4e

000007fe`f8249686 : 00000000`00000000 000007fe`cd11a308 00000000`ffa68ed8 00000000`0d37f2d0 : clr!COMToCLRWorkerBody+0x1ca

000007fe`f82495a2 : 00000000`00000002 00000000`00000000 00000000`0d37f3f0 00000000`00000000 : clr!COMToCLRWorkerBody+0xd6

000007fe`f8249e85 : 00000000`00000000 00000000`037733f0 00000000`00000002 00000000`00000000 : clr!COMToCLRWorkerDebuggerWrapper+0x22

000007fe`f8249b47 : 00000000`0e0936d0 00000000`0d37f2d0 00000000`012d8400 00000000`00000001 : clr!COMToCLRWorker+0x44b

000007fe`e1f122d6 : 00000000`0124fda0 00000000`01338310 00000000`00000002 00000000`0d37f330 : clr!GenericComCallStub+0x57

000007fe`e1f12e06 : 00000000`00000001 00000000`0e91aa10 00000000`00000000 00000000`0d37f950 : webengine4!HttpCompletion::ProcessRequestInManagedCode+0x1e8

000007fe`e1f12dc3 : 00000000`01338310 00000000`00000000 ffffffff`fffffffe 00000000`00000000 : webengine4!HttpCompletion::ProcessCompletion+0x5c

000007fe`f82413d5 : 000007fe`e1f12da0 00000000`0d37f870 00000000`00000000 000007fe`f89cdfa0 : webengine4!CorThreadPoolWorkitemCallback+0x24

000007fe`f8240908 : 00000000`0e0936d0 00000000`00000001 00000000`00000000 00000000`d73646db : clr!UnManagedPerAppDomainTPCount::DispatchWorkItem+0x181

000007fe`f834b7bf : 00000000`00000001 00000000`0ecd3c01 00000002`00000804 00000003`001a001a : clr!ThreadpoolMgr::NewWorkerThreadStart+0x2e5

000007fe`f8349b56 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : clr!ThreadpoolMgr::WorkerThreadStart+0x3b

00000000`76d9652d : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : clr!Thread::intermediateThreadProc+0x7d

00000000`76ecc521 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : kernel32!BaseThreadInitThunk+0xd

00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x1d

在堆栈中,以上标红的部分有一个Thread.Abort() 调用,这会引发TheadAbortException 异常的抛出。

2) 提取异常对象进行查看:

0:071> !do 00000001`5ff72ba8

Name:        System.Threading.ThreadAbortException

MethodTable: 000007fef72b6ef0

EEClass:     000007fef6df1860

Size:        160(0xa0) bytes

File:        C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

Fields:

MT    Field   Offset                 Type VT     Attr            Value Name

000007fef72b6738  400004b        8        System.String  0 instance 0000000000000000 _className

000007fef72b7760  400004c       10 ...ection.MethodBase  0 instance 0000000000000000 _exceptionMethod

000007fef72b6738  400004d       18        System.String  0 instance 0000000000000000 _exceptionMethodString

000007fef72b6738  400004e       20        System.String  0 instance 000000015ff72c48 _message

000007fef72d9bc8  400004f       28 ...tions.IDictionary  0 instance 0000000000000000 _data

000007fef72b6b00  4000050       30     System.Exception  0 instance 0000000000000000 _innerException

000007fef72b6738  4000051       38        System.String  0 instance 0000000000000000 _helpURL

000007fef72b5890  4000052       40        System.Object  0 instance 000000015ff72d50 _stackTrace

000007fef72b5890  4000053       48        System.Object  0 instance 0000000000000000 _watsonBuckets

000007fef72b6738  4000054       50        System.String  0 instance 0000000000000000 _stackTraceString

000007fef72b6738  4000055       58        System.String  0 instance 0000000000000000 _remoteStackTraceString

000007fef72bc620  4000056       88         System.Int32  1 instance                0 _remoteStackIndex

000007fef72b5890  4000057       60        System.Object  0 instance 0000000000000000 _dynamicMethods

000007fef72bc620  4000058       8c         System.Int32  1 instance      -2146233040 _HResult

000007fef72b6738  4000059       68        System.String  0 instance 0000000000000000 _source

000007fef72c3318  400005a       78        System.IntPtr  1 instance                0 _xptrs

000007fef72bc620  400005b       90         System.Int32  1 instance       -532462766 _xcode

000007fef72b6248  400005c       80       System.UIntPtr  1 instance      7fef71b5929 _ipForWatsonBuckets

000007fef72eb600  400005d       70 ...ializationManager  0 instance 000000015ff72c70 _safeSerializationManager

0:071> !do 000000015ff72c48

Name:        System.String

MethodTable: 000007fef72b6738

EEClass:     000007fef6e3ed68

Size:        40(0x28) bytes

File:        C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

String:      正在中止线程。

Fields:

MT    Field   Offset                 Type VT     Attr            Value Name

000007fef72bc620  4000103        8         System.Int32  1 instance                7 m_stringLength

000007fef72bb160  4000104        c          System.Char  1 instance             6b63 m_firstChar

000007fef72b6738  4000105       10        System.String  0   shared           static Empty

>> Domain:Value  00000000037733f0:000000015f980488 000000000383a7d0:000000015f980488 <<

3) 根据堆栈显示,异常对象抛出之后,被程序代码捕获并通过XXXXXX_Sql_Common!Common.ErrorLog.Insert() 记录下来:

000007fe`f398d1c8 : 00000001`5ff74050 00000000`00001296 00000001`5ff76c08 00000000`00000000 : clr!Buffer::BlockCopy+0x1a5

000007fe`f36b932a : 00006dae`d26d74e7 000007fe`f87cdfc0 00000000`0d37dce8 00000000`00000000 : System_Xml_ni!System.Xml.XmlTextReaderImpl.ShiftBuffer(Int32, Int32, Int32)+0x28

000007fe`f36b8c6d : 00000000`0d37b4d0 00000001`5ff76c08 00000014`00054cfe 000007fe`f718f13e : System_Xml_ni!System.Xml.XmlTextReaderImpl.ParseText(Int32 ByRef, Int32 ByRef, Int32 ByRef)+0x19a

000007fe`f36b8a87 : 00000002`3067ffe0 000007fe`f36c0cdb 000007fe`f37578c0 00000002`30680008 : System_Xml_ni!System.Xml.XmlTextReaderImpl.ParseText()+0x6d

000007fe`f36c02f9 : 00000001`5ff740b0 000007fe`f36bfd6d 00000001`5ff74060 00000001`5ff74050 : System_Xml_ni!System.Xml.XmlTextReaderImpl.ParseElementContent()+0x27

000007fe`f3703caa : 00000001`5ff73a30 00000002`6f9e4320 00000001`5ff74038 00000001`5ff74040 : System_Xml_ni!System.Xml.XmlLoader.LoadNode(Boolean)+0xf9

000007fe`f37036e0 : 00000001`5ff74038 00000001`5ff74040 00000001`5ff738e8 00000000`0d37b670 : System_Xml_ni!System.Xml.XmlLoader.LoadDocSequence(System.Xml.XmlDocument)+0x4a

000007fe`f370308d : 00000001`5ff738e8 00000002`7f992078 00000001`5ff738e8 00000000`0d37b6f0 : System_Xml_ni!System.Xml.XmlDocument.Load(System.Xml.XmlReader)+0x90

000007ff`004007f6 : 00000000`23a3db85 000007fe`f71f1590 00000000`00000000 00000000`00000000 : System_Xml_ni!System.Xml.XmlDocument.Load(System.String)+0xcd

000007ff`0040039c : 00000001`5ff732b0 00000002`6f9e4320 00000001`8f98ee20 80000000`00000001 : EasyMail_Sql_Common!Common.XmlHelper.InsertNode(System.String, System.String, Boolean, System.String, System.Collections.Hashtable, System.Collections.Hashtable)+0xb6

000007ff`00400060 : 00000002`0f980e00 00000001`5ff72c48 00000001`3f98b328 000007fe`f8266279 : EasyMail_Sql_Common!Common.ErrorLog.Insert(System.String, System.String, System.String)+0x30c

000007ff`004defbc : 00000002`0f980e00 00000001`5ff72ba8 00000001`3f98b328 00000000`0d37c2c0 : EasyMail_Sql_Common!Common.ErrorLog.Insert(System.String, System.Exception, System.String)+0x60

000007fe`f82665ed : 00000000`0e0936d0 00000000`0e0936d0 000007ff`004def74 00000000`0d37c370 : EasyMail_Web!EasyMail.Web.login.Page_Load(System.Object, System.EventArgs)+0x19ec

000007fe`f82663f2 : 00000000`0377e3a0 000007ff`004def74 00000000`0d37dea0 000007ff`004cbe98 : clr!ExceptionTracker::CallHandler+0x1c1

4) 通过进一步检查,发现记录日志的文件在D:\XXXX\SysLog\ErrorLog.XML

0:071> !do 00000002`6f9e4320

Name:        System.String

MethodTable: 000007fef72b6738

EEClass:     000007fef6e3ed68

Size:        86(0x56) bytes

File:        C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll

String:      D:\XXXX\SysLog\ErrorLog.XML

Fields:

MT    Field   Offset                 Type VT     Attr            Value Name

000007fef72bc620  4000103        8         System.Int32  1 instance               30 m_stringLength

000007fef72bb160  4000104        c          System.Char  1 instance               44 m_firstChar

000007fef72b6738  4000105       10        System.String  0   shared           static Empty

>> Domain:Value  00000000037733f0:000000015f980488 000000000383a7d0:000000015f980488 <<

原因分析:

通过以上分析步骤,可以了解到代码的执行流程大致如下:

a) XXXXXX_Web!XXXXXX.Web.login.Page_Load() 在执行HttpWebResponse.End() 时抛出了ThreadAbortException 异常;

b) 该异常抛出后被程序代码捕获;

c) 捕获的异常信息通过XXXXXX_Sql_Common!Common.ErrorLog.Insert() 方法写入到位于D:\XXXX\SysLog\ErrorLog.XML 的文件中;

首先,页面在执行代码时抛出异常就是不正确的。通过查询KB 了解到,这是一个已知问题:

http://support.microsoft.com/kb/312629

KB 中已经给出了不抛出异常的方法,即使用Response.Redirect() 方法的一个重载:

Response.Redirect(url, false);

其次,通过提取服务器上的日志文件,我们发现这个文件较大,约15MB。通过实际编写程序测试,在这个文件中插入一个异常信息,耗时较多:

一个由Response.Redirect 引起的性能问题的分析

这个15MB 的XML 文件在打开,保存是耗时较长。

结论:

由于登录页面在SSO 登录时采用了不恰当的页面跳转方法,导致页面抛出ThreadAbortException 异常,并引发页面打开一个体积较大的XML 文件进行读写。最终导致了页面跳转非常缓慢的现象。尤其是在多人同时访问时,导致了对XML 日志文件的争用。导致用户并发访问量越大,跳转速度越慢。

修复建议:

1) 使用Respons.Redirect(uri, false); 实现页面跳转;

2) 使用普通文本格式记录日志替代XML 格式,以便提高速度;

3) 使用Log4Net 等成熟日志组件记录日志;

上一篇:ThinkPHP 3.2版本 , 无法读取$_SESSION['verify_code']


下一篇:Python3求英文文档中每个单词出现的次数并排序