【问题标题】:How to tell why an IIS application pool is recycled如何判断 IIS 应用程序池被回收的原因
【发布时间】:2011-07-23 12:52:03
【问题描述】:

背景:

我已将可在我的机器上运行的 ASP.NET MVC 3 应用程序部署到 shared hosting provider 并发现一些似乎与正在回收的应用程序池有关的问题。主机已将回收配置为在以下任一情况下进行:

  • 内存使用量超过 200MB
  • CPU 使用率超过 75%(可能持续一段时间)
  • 20 分钟的空闲时间

我的开发机器上的限制更加宽松,所以我在开发过程中没有看到这样的回收。我没有对共享主机箱的管理员访问权限(可以理解),所以我无法阅读事件日志来了解为什么会发生这种回收。

问题:

有什么方法可以找出我的应用程序被回收的原因(例如在 Application_End 中),以便我可以记录它以帮助我进行调试?

【问题讨论】:

    标签: asp.net iis iis-7 asp.net-mvc-3 application-pool


    【解决方案1】:

    如果无法访问事件日志(因为您处于共享托管环境中),您将获得的最多信息来自 Application_End 事件并通过向 HttpRuntime(通过反射)询问值遗憾的是没有公开曝光的一两个私人成员。

    为此,请将以下代码添加到您的 Application_End 事件中:

    BindingFlags staticFlags = 
        BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.GetField;
    BindingFlags instanceFlags = 
        BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField;
    
    HttpRuntime runtime = (HttpRuntime)typeof(System.Web.HttpRuntime)
                            .InvokeMember("_theRuntime", staticFlags, null, null, null);
    if(runtime != null) 
    {
        string shutDownMessage = (string)runtime.GetType()
             .InvokeMember("_shutDownMessage", instanceFlags, null, runtime, null);
    
        string shutDownStack = (string)runtime.GetType()
             .InvokeMember("_shutDownStack", instanceFlags, null, runtime, null);
    
        // Log shutDownMessage & shutDownStack somewhere
    }
    

    如果我关闭或回收应用程序的应用程序池,我会看到以下内容:

    HostingEnvironment 启动关闭 HostingEnvironment 导致关机 - 在 System.Environment.GetStackTrace(异常 e,布尔需要文件信息) 在 System.Environment.get_StackTrace() 在 System.Web.Hosting.HostingEnvironment.InitiateShutdownInternal() 在 System.Web.Hosting.HostingEnvironment.InitiateShutdownWithoutDemand() 在 System.Web.Hosting.PipelineRuntime.StopProcessing()

    这可能已经差不多了。

    更新:

    我不记得在哪里找到了这段代码,但 Drew 提醒我这是来自 Scott Guthrie 的一篇博文。

    还有其他一些可能有用的私有成员,例如:

    private ApplicationShutdownReason _shutdownReason;
    

    您可以在 .NET Reflector 中检查这些字段(如果您仍有未定时炸弹的副本)或替代方法之一 (Open Source Alternatives to Reflector?)。

    【讨论】:

    • 您在ScottGu's blog 上找到这个了吗?我在发布问题之前尝试了这种方法,但它没有提供太多有用的数据。我收到的消息是 HostingEnvironment 启动关闭,这并不能帮助我弄清楚我需要做些什么来满足托管环境。
    • 一般来说,您可以通过System.Web.Hosting.HostingEnvironment.ShutdownReason 属性(它是一个枚举)访问这些数据。
    • @drew - 啊我不知道。
    • 没问题。顺便说一句,是 Scott Guthrie,而不是 Hanselman。我的第一条评论中有一个指向他博客文章的链接:weblogs.asp.net/scottgu/archive/2005/12/14/433194.aspx
    • 确实有一个可以用反射器看到的 _shutdownReason 枚举的详细注释。这是获取该枚举值的 sn-p:string shutDownReason = runtime.GetType().GetField("_shutdownReason", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField) .GetValue(runtime).ToString();
    【解决方案2】:

    研究 - 1

    首先我尝试使用System.Web.ProcessModelInfo.GetCurrentProcessInfo()System.Web.ProcessModelInfo.GetHistory(int)。这些方法的结果返回诸如 PID、开始时间、年龄、状态和峰值内存使用量等信息。不幸的是,这些在我的托管环境中不可用:

    HttpException 0x80004005 - 进程指标仅在启用 ASP.NET 进程模型时可用。在工作进程隔离模式下在 IIS 6 或更高版本上运行时,不支持此功能。

    这种方法可能对其他人有用,所以如果你处于这种情况,请试一试。

    研究 - 2

    System.Web.Hosting.HostingEnvironment.ShutdownReason 属性是一个有很多值的枚举,但不幸的是,我在问题中概述的所有情况都被捆绑到一个枚举值中:

    ApplicationShutdownReason.HostingEnvironment:宿主环境关闭了应用域。

    研究 - 3

    ScottGu has an approach on his blog(与Kev posted 相同的代码)使用反射来访问HttpApplication 的内部状态。不幸的是,在这种情况下,它只报告与上面 #2 相同的细节:

    _shutDownMessage =
      HostingEnvironment initiated shutdown
      HostingEnvironment caused shutdown
    
    _shutDownStack =
      at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)
      at System.Environment.get_StackTrace()
      at System.Web.Hosting.HostingEnvironment.InitiateShutdownInternal()
      at System.Web.Hosting.HostingEnvironment.InitiateShutdownWithoutDemand()
      at System.Web.Hosting.PipelineRuntime.StopProcessing()
    

    【讨论】:

    • 我不明白。您描述了一些在您的环境中不可用的东西,然后是一个没有帮助的枚举,以及一​​个没有帮助的反射属性。那么您的应用重启的最终原因是什么?
    【解决方案3】:

    下面是从https://mitchelsellers.com/blog/article/logging-asp-net-application-restarts找到的好代码

    //  obtain the shutdown reason
    System.Web.ApplicationShutdownReason shutdownReason = System.Web.Hosting.HostingEnvironment.ShutdownReason;
    string shutdownDetail = "";
    
    //Evaluate which option caused the error
    switch (shutdownReason)
    {
        case ApplicationShutdownReason.BinDirChangeOrDirectoryRename:
            shutdownDetail = "A change was made to the bin directory or the directory was renamed";
            break;
        case ApplicationShutdownReason.BrowsersDirChangeOrDirectoryRename:
            shutdownDetail = "A change was made to the App_browsers folder or the files contained in it";
            break;
        case ApplicationShutdownReason.ChangeInGlobalAsax:
            shutdownDetail = "A change was made in the global.asax file";
            break;
        case ApplicationShutdownReason.ChangeInSecurityPolicyFile:
            shutdownDetail = "A change was made in the code access security policy file";
            break;
        case ApplicationShutdownReason.CodeDirChangeOrDirectoryRename:
            shutdownDetail = "A change was made in the App_Code folder or the files contained in it";
            break;
        case ApplicationShutdownReason.ConfigurationChange:
            shutdownDetail = "A change was made to the application level configuration";
            break;
        case ApplicationShutdownReason.HostingEnvironment:
            shutdownDetail = "The hosting environment shut down the application";
            break;
        case ApplicationShutdownReason.HttpRuntimeClose:
            shutdownDetail = "A call to Close() was requested";
            break;
        case ApplicationShutdownReason.IdleTimeout:
            shutdownDetail = "The idle time limit was reached";
            break;
        case ApplicationShutdownReason.InitializationError:
            shutdownDetail = "An error in the initialization of the AppDomain";
            break;
        case ApplicationShutdownReason.MaxRecompilationsReached:
            shutdownDetail = "The maximum number of dynamic recompiles of a resource limit was reached";
            break;
        case ApplicationShutdownReason.PhysicalApplicationPathChanged:
            shutdownDetail = "A change was made to the physical path to the application";
            break;
        case ApplicationShutdownReason.ResourcesDirChangeOrDirectoryRename:
            shutdownDetail = "A change was made to the App_GlobalResources foldr or the files contained within it";
            break;
        case ApplicationShutdownReason.UnloadAppDomainCalled:
            shutdownDetail = "A call to UnloadAppDomain() was completed";
            break;
        default:
            shutdownDetail = "Unknown shutdown reason";
            break;
    }
    

    【讨论】:

      【解决方案4】:

      这个答案可以为那些在 IIS 7.x 或更高版本上遇到类似问题的人提供额外的见解。

      1.查找应用程序池何时开始关闭 - 以下代码可用于找出应用程序池何时开始关闭。实际关闭发生在此事件之后的最大关闭限制(秒,默认 90)内。

      public class ApplicationPoolService : IApplicationPoolService
      {
          public bool IsShuttingDown()
          {
              return System.Web.Hosting.HostingEnvironment.ShutdownReason != ApplicationShutdownReason.None;
          }
      
          public ApplicationShutdownReason GetShutdownReason()
          {
              return System.Web.Hosting.HostingEnvironment.ShutdownReason;
          }
      }
      
      public class HostingEnvironmentRegisteredObject : IRegisteredObject
      {
          public void Stop(bool immediate)
          {
              // second call is done when the Stop is imminent 
              if (immediate)
                  return;
      
              var reason = appPoolService.GetShutdownReason().ToString();
              logger.Log(LogLevel.Info, $"HostingEnvironmentRegisteredObject.stop called with shutdown reason {reason}");
          }
      }
      
      // this code should be placed in global.asax.cs
      protected void Application_Start()
      {
          HostingEnvironment.RegisterObject(new HostingEnvironmentRegisteredObject());
      }
      

      这有助于找到一般原因以及触发的确切时间。在您的情况下,我认为 HostingEnvironment 是价值。不幸的是,根本原因并不是唯一的。它可以是定期回收、由于内存限制而回收(OP 问题中最可能的原因)、由于固定时间而回收等。

      2。找到确切原因 - 找出确切原因的一种方法是在 EventLog 中搜索。如果无法访问,可以通过提供以下详细信息向托管服务提供商请求以缩小搜索范围。

      • 关闭启动的确切时间
      • 事件日志过滤器:
        • 事件源 = WAS
        • 事件级别 = 信息
        • 记录 = 自定义范围,包括确切的关机时间 +/- 1 分钟左右

      事件日志应返回更多相关信息,如下所示:

      进程 ID 为“xxx”的工作进程为应用程序池提供服务 'xxx' 已请求回收,因为它已达到 预定的回收时间。


      进程 ID 为“xxx”的工作进程为应用程序池提供服务 “xxx”已请求回收,因为它已到达其虚拟内存 限制。

      【讨论】:

        猜你喜欢
        • 2011-09-20
        • 2011-03-10
        • 1970-01-01
        • 2018-07-20
        • 1970-01-01
        • 2019-08-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多