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