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