Kent Sharkey 
Microsoft Corporation

2003 年 11 月

适用于:
    Microsoft® ASP.NET

摘要:学习如何创建 ASP.NET HTTP 处理程序,以查看 Web 站点使用的进程的运行状况和关闭状况。另外,还将学习如何创建配置节处理程序(本文包含一些指向英文站点的链接)。

下载 ProcessHandlerCS.msi 的 Visual C# 版本。

下载 ProcessHandlerVBSample.msi 的 Visual Basic 版本。(请注意,在示例文件中,程序员的注释使用的是英文,本文中将其译为中文是为了便于读者理解。)

目录
简介 
我们将看到什么?
创建进程查看处理程序 
实现 IHttpHandler 
安装 HTTP 处理程序 
添加配置 
小结

产品简介
您看到过出色的咖啡店店员送咖啡的情景吗?那简直就是咖啡豆、蒸汽和牛奶调和咖啡饮料在跳精彩的芭蕾,跳跃着奔向焦急等候的顾客。然而,即便是最好的店员偶尔也会出现问题。比如两个单子在处理时搞混了,结果送到您面前的是一杯 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 类的属性

属性 数据类型 说明 
Age TimeSpan 进程运行(或曾经运行)的总时间。如果这个值超出了在 Web.Config 文件的 processModel 节中的超时设置,可导致重新启动进程。 
PeakMemoryUsed Integer 此进程所用内存的最大值(以 MB 为单位)。如果这个值超出了在 Web.Config 文件的 processModel 节设置的 memoryLimit 级别设置,可导致进程重新启动。 
ProcessID Integer 操作系统使用此 ID 来标识进程。每个进程均有唯一的 ID(在进程运行时)。 
RequestCount Integer 进程接收到的页面请求的数量。如果这个值超出了在 Web.Config 文件的 processModel 中 requestLimit 的级别设置,可导致进程重新启动。 
ShutdownReason ProcessShutdownReason 此枚举定义进程重新启动的可能原因。有关可能的值,请参阅表 2。 
StartTime DateTime 进程启动的时间。 
Status ProcessStatus 此枚举定义 ASP.NET 辅助进程的当前状态。此值可能为 Alive(活动)、ShuttingDown(进程已接收到关闭请求)、ShutDown(进程已正常关闭)或 Terminated(进程已被迫关闭)。 

某进程关闭后,关闭原因将被设置为 ProcessShutdownReason Enumeration 中的某一个值。

表 2:进程关闭的可能原因

值 说明 
None 此值表明进程仍在运行。 
Timeout 进程因其生存期超出了在 Web.Config 文件的 processModel 节中设置的超时值而重启。如果这种情况频繁发生,也许应考虑增加超时值。不过,一般来说因这种原因而重新启动可以接受。 
IdleTimeout 进程重新启动的原因是缺少客户端。如果在 Web.Config 文件的 processModel 节中的 idleTimeout 值所设置的时间期限内没有客户端请求,将发生此类重新启动。这通常也是可以接受的重新启动的原因。 
RequestsLimit 进程重新启动的原因是接收到的请求数量超过了在 Web.Config 文件的 processModel 节中设置的值 (requestLimit)。一般来说,这种重新启动的原因也可以接受,主要用于您希望进程偶尔重新启动的情况。 
MemoryLimitExceeded 进程重新启动的原因是因为超出了通过 Web.Config 文件中的 memoryLimit 值设置的内存限制。这通常表示进程的某个 ASP.NET 应用程序部分发生了问题(可能是内存泄漏)。如果此类现象频繁发生,请监视每个 Web 应用程序的内存使用是否正常。 
RequestQueueLimit 进程重新启动的原因是在等候响应的请求总数超出了 Web.Config 文件的 requestQueueLimit 值。通常这是某些情况将导致 Web 服务器延迟的信号。可能需要增加内存或服务器,或提高驱动器或处理器的速度。 
DeadlockSuspected 进程重新启动的原因是可能停止了正在处理的请求。正如这个关闭原因的名称那样,最可能导致这种情况的原因为:如果两个或多个线程需要另一个线程完成后才能继续进行(例如 A 线程需要 B 线程完成向某文件的写入后才能继续进行,而同时 B 线程需要 A 线程完成计算后才能继续进行),我们将这种情况称为线程处于“Deadlock”(死锁)状态。如果有这种可能,进程将因此而关闭。一般来说,您肯定不希望看到这种关闭原因,如果您不幸看到了,请查看在应用程序中使用的所有线程处理或资源使用情况。 
PingFailed 当 ASP.NET 辅助进程管理页面时,有时会收到从 IIS 进程发来的 ping 以确定是否仍需要此进程。如果 ping 失败,则 IIS 进程可能会关闭该 ASP.NET 进程。这个关闭原因说明了可能在服务器接收消息的过程中确实存在通信问题或 ASP.NET 辅助进程因某种原因而停止工作。 
Unexpected 一般来说,您肯定不想看到此消息,因为它表明是“某种其他原因”终止了 ASP.NET 辅助进程。除了监视每个进程或对所有运行中的代码执行代码校对,几乎没有任何办法解决此问题。 

创建进程查看处理程序
在 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 接口的成员

成员 类型 说明 
IsReusable 属性 (Boolean) 确定该处理程序的实例是否可以重复使用。通常,该属性应返回 true,除非处理程序需要对某个资源的独占访问。 
ProcessRequest 方法 HTTP 处理程序的“主”方法。将在此添加对请求的所有处理。该类传递当前 ASP.NET 上下文。可以从此上下文中检索请求对象和响应对象。 

实现 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>\r\n</body>\r\n</html>