65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET 健康监控

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (6投票s)

2012年7月13日

CPOL

3分钟阅读

viewsIcon

46997

ASP.NET 健康监控

ASP.NET 健康监控是 ASP.NET 开发人员或 Web 服务器管理员经常忘记的框架瑰宝之一。它提供了出色的监控功能,通常允许您快速诊断失败的应用程序或系统。您是否曾经想过为什么 ASP.NET 错误会出现在系统事件日志中?这要归功于 ASP.NET 健康监控及其在 master web.config 文件(存储在框架安装目录中的文件)中的配置

<healthMonitoring>
    <bufferModes>...</bufferModes>
    <providers>...</providers>
    <eventMappings>...</eventMappings>
    <profiles>...</profiles>
    <rules>
        <add name="All Errors Default" 
            eventName="All Errors" provider="EventLogProvider"
            profile="Default" minInstances="1" 
            maxLimit="Infinite" minInterval="00:01:00"
            custom="" />
        <add name="Failure Audits Default" eventName="Failure Audits"
            provider="EventLogProvider" profile="Default" minInstances="1"
            maxLimit="Infinite" minInterval="00:01:00" custom="" />
    </rules>
</healthMonitoring>

让我们快速分析一下上面的片段。一个 rule 定义了哪些 ASP.NET 健康事件将被选定的 provider 记录 (eventname),以及记录的频率。ASP.NET 健康事件层次结构可以在 这里找到。 rule 定义中使用的 eventname 是在 web.config 文件中较早定义的映射的名称。示例事件映射如下所示

<eventMappings>
    <add name="All Errors" type="System.Web.Management.WebBaseErrorEvent,
     System.Web,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"
        startEventCode="0" endEventCode="2147483647" />
</eventMappings>

如果您回想起事件层次结构图,您会注意到 WebBaseErrorEventWebErrorEventWebRequestErrorEvent 的父级。因此,通过在规则的 eventname 属性的值中使用“所有错误”映射,我们通知 ASP.NET 框架,我们要记录所有 ASP.NET 错误事件。这是一个非常棒的功能,您可以使用它来创建自己的事件分组。其他与性能相关的事件包括

<add name="Heartbeats" type="System.Web.Management.WebHeartbeatEvent,
System.Web,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"
        startEventCode="0" endEventCode="2147483647" />
<add name="Application Lifetime Events" 
type="System.Web.Management.WebApplicationLifetimeEvent,System.Web,Version=4.0.0.0,
Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"
    startEventCode="0" endEventCode="2147483647" />
<add name="Request Processing Events" type="System.Web.Management.WebRequestEvent,
System.Web,Version=4.0.0.0,Culture=neutral,PublicKeyToken=b03f5f7f11d50a3a"
    startEventCode="0" endEventCode="2147483647" />

如果您也对安全审计感兴趣,请查看 WebAuditEvent 规范。让我们看看这在实际的 ASP.NET 代码中是如何工作的。我们将从一个简单的 Default.aspx 页面开始

<%@ Page Language="C#" AutoEventWireup="true" %>

<script runat="server">

  public void Page_Load() {
      Response.Write("OK");
  }
</script>

以及一个随后的 web.config 文件

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0">
    </compilation>

    <healthMonitoring enabled="true" heartbeatInterval="120">
      <rules>
        <add name="Heartbeats Default" eventName="Heartbeats" provider="EventLogProvider"
              profile="Default" />
        <add name="Application Lifetime Events Default" 
              eventName="Application Lifetime Events" 
              provider="EventLogProvider"
              profile="Default" />
        <add name="Request Processing Events Default" 
              eventName="Request Processing Events" 
              provider="EventLogProvider"
              profile="Default" />
      </rules>
    </healthMonitoring>
  </system.web>
</configuration>

如果您将此代码部署到服务器并调用 Default.aspx 页面,则新事件应出现在事件日志中。代码为 10011002 的事件提供有关应用程序启动和关闭的信息。每 120 秒(heartbeatInterval="120")发出的代码为 1005 的事件揭示了应用程序状态

看看这个简单的事件暴露了多少信息。例如,我们可以轻松地说出我们的应用程序是否消耗了太多内存,或者在为用户请求提供服务时是否存在任何延迟(Requests queued)。如果发现任何问题,我们可以进一步分析出现问题的机器上的应用程序(Machine name, Process ID)。例如,如果您的应用程序与其他应用程序共享一个应用程序池,则可能需要检查内存问题是否不是由其中一个应用程序引起的。

在本段中,我将选择几个事件,向您展示 ASP.NET 框架的哪一部分触发了它们。我通过简单地在 System.Web.Management.EventLogWebEventProvider.ProcessEvent 方法上放置一个断点来发现这一点。一旦命中断点,我就从调试器中收集了一个托管调用堆栈。例如,WebApplicationLifetimeEvent 的调用堆栈显示,当应用程序启动以处理第一个请求时,是 HttpApplicationFactory 触发了事件

System.Web.dll!System.Web.Management.EventLogWebEventProvider.ProcessEvent
(System.Web.Management.WebBaseEvent eventRaised = 
       {System.Web.Management.WebApplicationLifetimeEvent}) + 0x13 bytes	
System.Web.dll!System.Web.Management.WebBaseEvent.RaiseInternal(System.Web.Management.WebBaseEvent 
       eventRaised = {System.Web.Management.WebApplicationLifetimeEvent}, System.Collections.ArrayList 
       firingRuleInfos = Count = Cannot evaluate expression because the code 
                                 of the current method is optimized., 
       int index0, int index1) + 0x265 bytes	
System.Web.dll!System.Web.Management.WebBaseEvent.RaiseSystemEventInternal
      (string message, object source, 
      int eventCode, int eventDetailCode, System.Exception exception, string nameToAuthenticate) + 
      0xef bytes	
System.Web.dll!System.Web.HttpApplicationFactory.EnsureAppStartCalled
(System.Web.HttpContext context = {System.Web.HttpContext}) + 0xaf bytes	
System.Web.dll!System.Web.HttpApplicationFactory.GetApplicationInstance
(System.Web.HttpContext context = {System.Web.HttpContext}) + 0x5d bytes	
System.Web.dll!System.Web.HttpRuntime.ProcessRequestInternal
(System.Web.HttpWorkerRequest wr = {Microsoft.VisualStudio.WebHost.Request}) + 0x20e bytes	
System.Web.dll!System.Web.HttpRuntime.ProcessRequestNoDemand
(System.Web.HttpWorkerRequest wr) + 0x6e bytes	
System.Web.dll!System.Web.HttpRuntime.ProcessRequest(System.Web.HttpWorkerRequest wr) + 0x47 bytes

WebHeartbeatEvent 调用堆栈中,我们可以读到它是由计时器(在专用的系统线程上)触发的

System.Web.dll!System.Web.Management.EventLogWebEventProvider.ProcessEvent
(System.Web.Management.WebBaseEvent eventRaised = 
         {System.Web.Management.WebHeartbeatEvent}) + 0x13 bytes
System.Web.dll!System.Web.Management.WebBaseEvent.RaiseInternal
(System.Web.Management.WebBaseEvent eventRaised = 
         {System.Web.Management.WebHeartbeatEvent}, 
         System.Collections.ArrayList firingRuleInfos = Count = 
         Cannot evaluate expression because the code of the current method is optimized., 
         int index0, int index1) + 0x265 bytes
System.Web.dll!System.Web.Management.WebBaseEvent.RaiseSystemEventInternal(string message, 
          object source, int eventCode, int eventDetailCode, 
          System.Exception exception, string nameToAuthenticate) + 0xef bytes
System.Web.dll!System.Web.Management.HealthMonitoringManager.HeartbeatCallback(object state) + 
0x27 bytes
mscorlib.dll!System.Threading.TimerQueueTimer.CallCallbackInContext(object state) + 0x2c bytes
mscorlib.dll!System.Threading.ExecutionContext.RunInternal
(System.Threading.ExecutionContext executionContext, 
          System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) + 0xa7 bytes
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, 
          System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) + 0x16 bytes
mscorlib.dll!System.Threading.TimerQueueTimer.CallCallback() + 0x71 bytes
mscorlib.dll!System.Threading.TimerQueueTimer.Fire() + 0xc0 bytes
mscorlib.dll!System.Threading.TimerQueue.FireNextTimers() + 0x131 bytes
mscorlib.dll!System.Threading.TimerQueue.AppDomainTimerCallback() + 0x1c bytes

最后,WebRequestErrorEvent 在请求处理完成后由 HttpRuntime 触发

System.Web.dll!System.Web.Management.EventLogWebEventProvider.ProcessEvent
(System.Web.Management.WebBaseEvent eventRaised = 
         {System.Web.Management.WebRequestErrorEvent}) + 0x13 bytes	
System.Web.dll!System.Web.Management.WebBaseEvent.RaiseInternal(System.Web.Management.WebBaseEvent 
          eventRaised = {System.Web.Management.WebRequestErrorEvent}, 
          System.Collections.ArrayList firingRuleInfos 
          = Count = Cannot evaluate expression because the code of the current method is optimized., 
          int index0, int index1) + 0x265 bytes	
System.Web.dll!System.Web.Management.WebBaseEvent.RaiseSystemEventInternal
          (string message, object source, 
          int eventCode, int eventDetailCode, System.Exception exception, 
          string nameToAuthenticate) + 0xef bytes	
System.Web.dll!System.Web.Management.WebBaseEvent.RaiseRuntimeError
(System.Exception e, object source) + 0xa3 bytes	
System.Web.dll!System.Web.HttpResponse.ReportRuntimeError
(System.Exception e = {Cannot evaluate expression because 
          the code of the current method is optimized.}, 
          bool canThrow = true, bool localExecute = false) + 0x58 bytes	
System.Web.dll!System.Web.HttpRuntime.FinishRequest
(System.Web.HttpWorkerRequest wr = {Microsoft.VisualStudio.WebHost.Request}, 
          System.Web.HttpContext context = {System.Web.HttpContext}, System.Exception e) + 0x16e bytes	
System.Web.dll!System.Web.HttpRuntime.OnHandlerCompletion(System.IAsyncResult ar) + 0x9c bytes	
System.Web.dll!System.Web.HttpAsyncResult.Complete(bool synchronous, object result, 
          System.Exception error, System.Web.RequestNotificationStatus status) + 0x3a bytes	
System.Web.dll!System.Web.HttpApplication.ApplicationStepManager.ResumeSteps
(System.Exception error) + 0x288 bytes	
System.Web.dll!System.Web.HttpApplication.System.Web.IHttpAsyncHandler.BeginProcessRequest(
          System.Web.HttpContext context, System.AsyncCallback cb, object extraData) + 0xf8 bytes	
System.Web.dll!System.Web.HttpRuntime.ProcessRequestInternal(System.Web.HttpWorkerRequest wr = 
         {Microsoft.VisualStudio.WebHost.Request}) + 0x284 bytes	
System.Web.dll!System.Web.HttpRuntime.ProcessRequestNoDemand(System.Web.HttpWorkerRequest wr) + 
0x6e bytes	
System.Web.dll!System.Web.HttpRuntime.ProcessRequest(System.Web.HttpWorkerRequest wr) + 0x47 bytes

您现在可能会问自己,这种监控对您的应用程序性能有何影响。不幸的是,我无法给您确切的数字,但我向您保证,微软已尽最大努力使其尽可能不可见 Smile。我认为最重要的性能相关决定是选择将收集健康事件的提供程序。开箱即用有几个提供程序,包括 EventLogWebEventProviderIisTraceWebEventProviderTraceWebEventProviderSqlWebEventProvider。例如,我建议使用 SqlWebEventProvider 来存储不经常发生的事件,例如审计、错误或应用程序生命周期事件 - 将它们保存在 SQL Server 表中可能有助于您将来生成一些与性能相关的报告。对于更频繁发生的事件(例如心跳Web 请求事件),您应该寻找能够以微不足道的延迟消耗大量数据的提供程序 - 我认为 TraceWebEventProviderIisTraceWebEventProvider 甚至 EventLogWebEventProvider 将在这里占据主导地位。

总而言之,健康监控事件提供了对应用程序生命周期的极好见解。通过正确处理它们,您可以轻松监控您的服务器 Web 场并在它们成为主要问题之前发现任何异常。如果您想使用这项技术,请获取 我的示例应用程序并根据您的需要进行调整。

© . All rights reserved.