1. 問題起因
2. 嘗試過的處理思路
3. 解決方法
1. 問題起因
一個從VS2003(.Net Framework 1.1)升級到.net framework 4.0的項目,每次編譯或者部署到服務器上后,首次請求任何一個目錄下的默認頁面時,都要耗時3~5秒;而以前使用.net framework 1.1的時候,沒有這個問題。
我在頁面上開啟Trace="true"來跟蹤,發現頁面的處理時間並不久(IIS重啟,首次打開頁面時截獲的信息):
但是IIS日志中,顯示首次請求頁面的耗時接近4.635秒。后續連續刷新頁面,處理時間比較正常。。
雖然耗時比較久,但頁面能夠打開;然后我重啟IIS,再次打開站點,用VS附加W3WP.exe來進行調試,再次打開這個頁面的時候,發現拋出異常了:發生了 System.ArgumentException, Message=已存在具有相同鍵的條目。
1: 發生了 System.ArgumentException
2: Message=已存在具有相同鍵的條目。
3: Source=System
4: StackTrace:
5: 在 System.Collections.Specialized.ListDictionary.Add(Object key, Object value)
6: InnerException:
7: System.dll!System.Collections.Specialized.ListDictionary.Add(object key, object value) + 0x134 字節
8: System.Web.dll!System.Web.UI.ParsedAttributeCollection.AddFilteredAttribute(string filter, string name, string value) + 0xba 字節
9: System.Web.dll!System.Web.UI.TemplateParser.ProcessAttributes(System.Text.RegularExpressions.Match match, out System.Web.UI.ParsedAttributeCollection attribs = {System.Web.UI.ParsedAttributeCollection}, bool fDirective = false, out string duplicateAttribute = null) + 0x221 字節
10: System.Web.dll!System.Web.UI.TemplateParser.ProcessBeginTag(System.Text.RegularExpressions.Match match = {System.Text.RegularExpressions.Match}, string inputText) + 0x68 字節
11: System.Web.dll!System.Web.UI.TemplateParser.ParseStringInternal(string text, System.Text.Encoding fileEncoding) + 0x3d0 字節
12: System.Web.dll!System.Web.UI.TemplateParser.ParseString(string text, System.Web.VirtualPath virtualPath, System.Text.Encoding fileEncoding) + 0x6f 字節
13: System.Web.dll!System.Web.UI.TemplateParser.ParseFile(string physicalPath, System.Web.VirtualPath virtualPath) + 0x115 字節
14: System.Web.dll!System.Web.UI.TemplateParser.ParseInternal() + 0x57 字節
15: System.Web.dll!System.Web.UI.TemplateParser.Parse() + 0x64 字節
16: System.Web.dll!System.Web.Compilation.BaseTemplateBuildProvider.CodeCompilerType.get() + 0x6f 字節
17: System.Web.dll!System.Web.Compilation.BuildProvider.GetCompilerTypeFromBuildProvider(System.Web.Compilation.BuildProvider buildProvider) + 0x42 字節
18: System.Web.dll!System.Web.Compilation.WebDirectoryBatchCompiler.CompileNonDependentBuildProviders(System.Collections.ICollection buildProviders) + 0xca 字節
19: System.Web.dll!System.Web.Compilation.WebDirectoryBatchCompiler.Process() + 0x5d 字節
20: System.Web.dll!System.Web.Compilation.BuildManager.BatchCompileWebDirectoryInternal(System.Web.Hosting.VirtualDirectory vdir, bool ignoreErrors) + 0x48 字節
21: System.Web.dll!System.Web.Compilation.BuildManager.BatchCompileWebDirectory(System.Web.Hosting.VirtualDirectory vdir, System.Web.VirtualPath virtualDir, bool ignoreErrors) + 0xbc 字節
22: System.Web.dll!System.Web.Compilation.BuildManager.CompileWebFile(System.Web.VirtualPath virtualPath = {System.Web.VirtualPath}) + 0x5d 字節
23: System.Web.dll!System.Web.Compilation.BuildManager.GetVPathBuildResultInternal(System.Web.VirtualPath virtualPath = {System.Web.VirtualPath}, bool noBuild, bool allowCrossApp, bool allowBuildInPrecompile, bool throwIfNotFound, bool ensureIsUpToDate) + 0x141 字節
24: System.Web.dll!System.Web.Compilation.BuildManager.GetVPathBuildResultWithNoAssert(System.Web.HttpContext context, System.Web.VirtualPath virtualPath, bool noBuild, bool allowCrossApp, bool allowBuildInPrecompile, bool throwIfNotFound, bool ensureIsUpToDate) + 0x70 字節
25: System.Web.dll!System.Web.Compilation.BuildManager.GetVirtualPathObjectFactory(System.Web.VirtualPath virtualPath, System.Web.HttpContext context, bool allowCrossApp, bool throwIfNotFound) + 0x7e 字節
26: System.Web.dll!System.Web.Compilation.BuildManager.CreateInstanceFromVirtualPath(System.Web.VirtualPath virtualPath, System.Type requiredBaseType = {Name = "Page" FullName = "System.Web.UI.Page"}, System.Web.HttpContext context = {System.Web.HttpContext}, bool allowCrossApp) + 0x35 字節
27: System.Web.dll!System.Web.UI.PageHandlerFactory.GetHandlerHelper(System.Web.HttpContext context, string requestType, System.Web.VirtualPath virtualPath = {System.Web.VirtualPath}, string physicalPath) + 0x20 字節
28: System.Web.dll!System.Web.UI.PageHandlerFactory.System.Web.IHttpHandlerFactory2.GetHandler(System.Web.HttpContext context, string requestType, System.Web.VirtualPath virtualPath, string physicalPath) + 0x29 字節
29: System.Web.dll!System.Web.HttpApplication.MapHttpHandler(System.Web.HttpContext context, string requestType, System.Web.VirtualPath path, string pathTranslated = "。。。。。。。。。。\\order\\default.aspx", bool useAppConfig) + 0xa8 字節
30: System.Web.dll!System.Web.HttpApplication.MapHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() + 0x81 字節
31: System.Web.dll!System.Web.HttpApplication.ExecuteStep(System.Web.HttpApplication.IExecutionStep step = {System.Web.HttpApplication.MapHandlerExecutionStep}, ref bool completedSynchronously = true) + 0xb9 字節
32: System.Web.dll!System.Web.HttpApplication.ApplicationStepManager.ResumeSteps(System.Exception error) + 0x13e 字節
33: System.Web.dll!System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(System.Web.HttpContext context, System.AsyncCallback cb, object extraData) + 0xf8 字節
34: System.Web.dll!System.Web.HttpRuntime.ProcessRequestInternal(System.Web.HttpWorkerRequest wr = {System.Web.Hosting.ISAPIWorkerRequestInProcForIIS7}) + 0x1a2 字節
35: System.Web.dll!System.Web.HttpRuntime.ProcessRequestNoDemand(System.Web.HttpWorkerRequest wr) + 0x7d 字節
36: System.Web.dll!System.Web.Hosting.ISAPIRuntime.ProcessRequest(System.IntPtr ecb, int iWRType) + 0xfd 字節
37: [應用程序域轉換]
38: [本機到托管的轉換]
2. 嘗試過的處理思路
我Attach到w3wp上去后,首次訪問目錄下的默認頁面,catch到ArgumentException異常,然后根據異常信息,追蹤到 TemplateParser.ParseFile -> ParseString -> ParseStringInternal -> ProcessBeginTag;然后我就嘗試了下列努力:
1. 該目錄下默認文件的<%@Page ....%>定義,屬性與其他文件並沒有任何不同(除了CodeBehind和Inherits的值),我把頁面里面的內容一點兒一點兒刪掉,最后是剩下空內容(只剩下head、body、form幾個元素)、空后台代碼,問題還依舊存在。
2. 我在這個目錄下面,新建了一個頁面(VS2010自動生成的,沒有添加任何元素或標簽),然后把這個新文件作為該目錄默認頁,發現問題還是存在。
3. 在第2步的基礎上,打開新默認頁后,我在URL中直接輸入舊的默認頁,頁面瞬間刷出來了。然后我就糾結了,這延時,與頁面內容毛的關系都木有,這異常信息也太坑爹了。
4. 出於嘗試一下,我試下該目錄下的其他文件,首次訪問會不會變慢。於是重啟IIS,然后發現首次訪問該目錄中的第一個文件(不論首次訪問的是哪個文件),都會耗時數秒;如果Attach到w3wp,都會Catch到這個異常。
5. 項目是從.net framework 1.1(VS2003)上直接升級過來的,以前03的版本,木有這個問題。然后google“asp.net 4.0 slow”,找到下面這個:
Slow Performance — ASP .NET ASPNET_WP.EXE and CSC.EXE Running After Clicking Redirect Link
6. The very first time that the page is load, then the asp.net compile a lot of pages, almost every one found on the same dir, including modules, and dlls found on bin.也就是說,在首次請求一個目錄下的某個文件時,會把該目錄的所有文件都編譯一下。這樣做的好處是,把一個目錄的編譯操作都集中在首次訪問上,后續請求會比較快;但這樣帶來的問題是,如果編譯時間比較久,那第一個發起請求的人就杯具了。於是在配置文件中,設置batch="false"
,然后問題消失,首次請求的速度提上來了,attach上去也沒有異常了。
3. 解決方法
部署的時候,記得在配置文件中,把debug和batch關閉,把optimizeCompilations打開:
<compilation targetFramework="4.0" debug="false" batch="false" optimizeCompilations="true">