microsoft® asp.net 在系統可靠性方面取得了優于其任何競爭對手的巨大進步。然而,就像那位出色的店員一樣,asp.net 偶爾也會出現問題。幸運的是,asp.net 是非常優秀的服務器。它能在后臺迅速生成新的進程,然后處理請求。通常只會在請求頁面時發生一點用戶甚至可能都不會注意到的輕微延遲。
而 asp.net 系統的管理員可能需要知道發生了什么。同樣,他們也想了解是什么原因導致了進程失敗。幸運的是,使用 .net framework 類庫文檔中的 processinfo 和 processmodelinfo 類便可獲得相關信息。本文中,我們將學習如何創建 asp.net http 處理程序,以使用這些對象查看 web 站點使用的進程的運行狀況和關閉狀況。另外,我們將創建一個配置節處理程序,這樣我們便能夠在安裝處理程序后對其進行配置。
我們將看到什么? asp.net 進程負責編譯和管理所有向 asp.net 頁面提出的請求。理想狀況下,此進程應該始終存在于服務器中:活躍地接收請求、編譯頁面并返回 html。然而,由于存在許多可能影響進程的潛在事件,我們不得不面對 web 開發的真實狀況。開發人員可能未能正確處理內存泄漏或線程問題;服務器可能會丟失與進程的連接;或者甚至會因為在 web.config 文件的 <processmodel> element 節中對 idletimeout、requestlimit、memorylimit 和類似的項目進行了錯誤的配置而導致出現問題。如果發生了以上任何一種事件,則將創建新的 asp.net 輔助進程,新的請求將移交至此進程進行處理。
由于 asp.net 進程對頁面處理如此重要,因此監視這些進程同樣重要。使用 processinfo 和 processmodelinfo 類可以查看當前和以前進程的有效期和運行狀況。圖 1 所示為在本文中創建的進程列表。
圖 1:web 服務器的進程歷史記錄
processinfo class 存儲了給定進程的數據。不得自行創建 processinfo 類,但可以使用 processmodelinfo class 來檢索 processinfo 對象。表 1 所示為 processinfo 類的重要屬性。
public sub processrequest(byval context as httpcontext) _ implements ihttphandler.processrequest _context = context _writer = new htmltextwriter(context.response.output)
'we only want to do this if we're enabled if _config.enabled then _writer.writeline("<html>") _writer.writeline("<head>") _writer.writeline(me.stylesheet) _writer.writeline("</head>")
'write content here 'create table dim t as new table() with t .width = unit.percentage(100) .cellpadding = 0 .cellspacing = 0 end with
'the meat of the routine 'make certain this is a destination machine if (permittedhost(_context.request.userhostaddress)) then createheader(t) addprocesses(t) createfooter(t) else createerrorreport(t) end if
'write to the stream t.rendercontrol(_writer)
_writer.writeline("</span>/r/n</body>/r/n</html>") end if end sub
processrequest 的實現會存儲當前上下文和編寫者。然后,它通過呈現頁面的起始 html 標簽將新的 html 頁面創建為輸出。下一步,它將創建一個用于格式化輸出的表格。最后,如果啟用了處理程序,并且發出請求的客戶端是合法的 ip 地址之一,則通過以下三種方法創建輸出: createheader、addprocesses 和 createfooter。這些方法將相應的值呈現在表格的單元格中。這些代碼有很大一部分是重復的,為了簡短起見,以下僅給出了 addprocesses 及其相關的方法。
private sub addprocesses(byval table as _ system.web.ui.webcontrols.table) dim procs as processinfo() = _ processmodelinfo.gethistory(_config.requestlimit) dim row as tablerow
public function create(byval parent as object, _ byval configcontext as object, _ byval section as system.xml.xmlnode) as object _ implements configuration.iconfigurationsectionhandler.create ' 節具有以下格式: '<processview ' localonly="true|false" ' requestlimit="<=100" ' enabled="true|false" ' permittedhosts="comma-delimited list of ip addresses" /> dim result new processviewerconfiguration() dim config as new configurationhelper(section)
dim max as integer dim hosts as string const delimiter as string = ", " const maximumreturncount as integer = 100 '確認設置,并設定 result.enabled = config.getbooleanattribute("enabled") result.localonly = config.getbooleanattribute("localonly")
max = config.getintegerattribute("requestlimit") if max <= maximumreturncount then result.requestlimit = max end if
hosts = config.getstringattribute("permittedhosts") result.permittedhosts = hosts.split(delimiter.tochararray()) return result end function
friend class configurationhelper dim _section as xmlnode public sub new(byval configsection as xmlnode) _section = configsection end sub '接受 true/false、yes/no public function getbooleanattribute(byval name as string) as boolean dim value as string dim result as boolean
value = getstringattribute(name).tolower() if ((boolean.truestring.tolower() = value) _ orelse (value = "yes")) then result = true else result = false end if
return result end function
public function getintegerattribute(byval name as string) as integer dim value as string dim result as integer
value = getstringattribute(name) result = int32.parse(value)
return result end function
public function getstringattribute(byval name as string) as string dim theattribute as xmlattribute dim result as string
theattribute = _section.attributes(name) if not theattribute is nothing then result = theattribute.value end if return result end function