创建ASP.NET监视服务器进程
产品简介您看到过出色的咖啡店店员送咖啡的情景吗?那简直就是咖啡豆、蒸汽和牛奶调和咖啡饮料在跳精彩的芭蕾,跳跃着奔向焦急等候的顾客。然而,即便是最好的店员偶尔也会出现问题。比如两个单子在处理时搞混了,结果送到您面前的是一杯 Soy latte。也可能是杯子上龙飞凤舞的潦草字迹根本就是写错了,或者店员理解错了。有人要了一杯“卞高奇若”(卡普其诺),可怜的店员绞尽脑汁也弄不懂顾客到底要点什么。如果出现了类似的问题,就必须停止处理,然后再重新开始。好的服务员可能会推延一下现有的要求,而优秀的服务员却能够在没人察觉的情况下做到这一点。 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 类的重要属性。 表 1:ProcessInfo 类的属性
某进程关闭后,关闭原因将被设置为 ProcessShutdownReason Enumeration 中的某一个值。 表 2:进程关闭的可能原因
创建进程查看处理程序在 ASP.NET 中,主要使用两种方法来创建 HTTP 处理程序。第一种是通过创建带有 ASHX 扩展名的文件,另一种是创建实现 System.Web.IHttpHandler 的类,请参阅 IHttpHandler Interface。本文将着重介绍第二种形式。要创建 HTTP 处理程序,需要创建一个程序集(通常是一个代码库项目)和一个实现 System.Web.IHttpHandler 的类。然后将该类注册到 Web.Config(或 machine.config)文件中,然后它就可以接收请求了。如果查看 machine.config 文件(在相应命名的 httpHandlers 节中),将看到许多当前已注册的 HTTP 处理程序,包括 System.Web.UI.PageHandlerFactory(ASP.NET 页面的主处理程序)。在编写 HTTP 处理程序时,其实就是在定义处理请求的新方法。 所有 HTTP 处理程序均通过实现 System.Web.IHttpHandler Interface 来创建。此接口需要创建一个属性和一个方法,如表 3 所示。 表 3:IHttpHandler 接口的成员
实现 IHttpHandler创建 HTTP 处理程序的大量工作集中在实现处理程序的 ProcessRequest。通常,需要存储当前上下文的请求和响应对象,然后使用响应对象的编写方法创建输出。以下给出了用于进程查看处理程序的 ProcessRequest 资源的 Microsoft Visual Basic⮠.NET 源。 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>")
_writer.WriteLine("<body>")
_writer.WriteLine("<span class=""content"">")
"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>
</body>
</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
_list = New ProcessInfoCollection
For Each proc As ProcessInfo In procs
row = AddRow(table)
_list.Add(proc)
AddCell(row, proc.ProcessID.ToString())
AddCell(row, proc.Status.ToString())
AddCell(row, proc.StartTime.ToString("g"))
AddCell(row, FormatAge(proc.Age))
AddCell(row, proc.PeakMemoryUsed.ToString("N0") + " MB")
AddCell(row, proc.RequestCount.ToString("N0"))
AddCell(row, proc.ShutdownReason.ToString())
Next
End Sub
Private Function AddCell( _
ByVal row As System.Web.UI.WebControls.TableRow, _
ByVal text As String) As System.Web.UI.WebControls.TableCell
Dim c As New TableCell()
c.Text = text
row.Cells.Add⩍
Return c
End Function
细心的(和有技术背景的)读者可能已经注意到,我完全可以通过呈现 DataGrid 并将 ProcessInfoCollection 绑定到 DataGrid 来简化此代码,但那样就失去了编写程序的乐趣。 安装 HTTP 处理程序创建完 HTTP 处理程序后,必须进行安装才能使用。这包括使类可用,并在配置文件中添加相应的信息以激活处理程序。 如果创建的是仅被单个 vroot 使用的简单处理程序,则可以将 DLL 复制至该 vroot 的 bin 目录即可使用该类。如果创建了一个由多个 vroot 使用的 HTTP 处理程序(类似于 ProcessHandler),则此处理程序必须安装到全局程序集缓存 (GAC) 中。要将此处理程序安装到 GAC 中,类必须具有严格名称。要具有严格名称,它必须有关联的严格名称键。必须使用命令行可执行文件 sn.exe 创建严格名称键文件。有关此程序的详细信息,请参阅 NET Framework Tools 文档的 Strong Name Tool (Sn.exe) 一节。 处理程序可用后,下一步就是添加配置以使其可以处理请求,方法是在 Web.Config 或 machine.config 文件的 httpHandlers 节中添加条目。此条目指定了将通过处理程序路由的文件扩展名和操作。进程查看处理程序的条目如下所示。 <add verb="*" path="process.axd"
type="Microsoft.Samples.Msdn.Web.ProcessHandler,
MsdnProcessHandler, Version=1.0.0.0, Culture=neutral,
PublicKeyToken=f5f94c20bb90ce64" />
此条目意味着在某个请求使用任何 HTTP 命令寻找“文件” process.axd(实际上不存在)时,它将向位于程序集 MsdnProcessHandler 中的 Microsoft.Samples.Msdn.Web.ProcessHandler 类发送请求。该类将实现 IHttpHandler,然后由 IHttpHandler 负责生成输出。 添加配置许多 ASP.NET 应用程序使用 appSetting 标签添加自定义配置。这对于大多数应用程序来说已经完全足够了。然而,有时应用程序可以使用更有针对性的解决方案。这种情况下,您可以为应用程序新建节。 新建配置节包括两个步骤。首先,必须创建配置对象。此对象或结构具有表示所需配置数据的属性。此对象可以具有、但通常不具有任何方法。其次要创建一个节处理程序。此节处理程序负责从 web.congfig 文件中读取相应的信息,并且将其转化为配置对象。 ProcessViewer 的配置对象具有四个属性,如下表所述。 表 4:ProcessViewer 配置对象的属性
创建自定义配置的第二步是创建解释配置文件的 XML 的类,并使用该信息填充配置对象。此类必须实现 System.Configuration.IConfigurationSectionHandler 接口。此接口只有一个方法,称为 Create。以下给出了 ProcessViewerSectionHandler 的 Visual Basic .NET 源(请参阅 C# 源的下载)。 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
Create 方法传递了三个对象:
以上代码使用了 ConfigurationHelper 对象。此对象是一个用于从节中检索特定数据类型的简单对象。此类的代码如下所示。 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
End Class
要使用这个节处理程序和配置对象,必须将其注册在相应的 ASP.NET 配置文件中。因为可以在任何进程中调用该类,所以最好将其添加到 machine.config 文件中。在 machine.config 类的 configSection 节中的相应位置注册节处理程序(我将其添加到了 system.web 节中) <sectionGroup name="system.web">
... other sections
<section name="processView"
type="Microsoft.Samples.Msdn.Web.ProcessViewerSectionHandler,
MsdnProcessHandler, Version=1.0.0.0,
Culture=neutral,
PublicKeyToken=f5f94c20bb90ce64" />
</sectionGroup>
注册完毕后,便可以向 machine.config 文件中添加节,并且该类将成为可配置类。 小结创建 HTTP 处理程序可以提供超出 ASP.NET. 功能的强有力机制,使得开发者避开页面模型,并创建、修改或扩展 Web 站点的内容。通过添加用于查看 ASP.NET 的进程历史记录的 HTTP 处理程序,可以诊断代码或服务器中导致客户投诉的问题,例如代码中存在内存泄漏或未处理的异常,或服务器的内存不足。 创建并安装 HTTP 处理程序后,您将可以更敏锐地发现在此重要进程中发生的状况。到时候您就会有时间来喝杯咖啡,而不是忙于追查 Web 站点进程重新启动的原因了。 源代码网供稿. |

