简体   繁体   English

如何判断为什么要回收 IIS 应用程序池

[英]How to tell why an IIS application pool is recycled

Background:背景:

I've deployed an ASP.NET MVC 3 app that works on my machine to a shared hosting provider and am discovering some problems that appear to be related to the application pool being recycled.我已将在我的机器上运行的 ASP.NET MVC 3 应用程序部署到共享托管服务提供商,并发现了一些似乎与正在回收的应用程序池相关的问题。 The host has configured recycling to occur under any of these circumstances:主机已将回收配置为在以下任一情况下发生:

  • Memory usage exceeds 200MB内存使用量超过 200MB
  • CPU usage exceeds 75% (presumably over a sustained period) CPU 使用率超过 75%(大概是持续一段时间)
  • 20 minutes of idle time 20分钟空闲时间

The restrictions are more relaxed on my development machine so I wasn't seeing recycling like this during development.我的开发机器上的限制更加宽松,所以我在开发过程中没有看到这样的回收。 I don't have admin access to the shared hosting box (understandably) so I can't read the event log to see why this recycling is occurring.我没有对共享主机盒的管理员访问权限(可以理解),所以我无法阅读事件日志以了解为什么会发生这种回收。

Question:题:

Is there a way I can find out why my app was recycled (in Application_End for example) so that I can log it to help my debugging?有没有办法找出我的应用程序被回收的原因(例如在Application_End中),以便我可以记录它以帮助我的调试?

Without access to the event logs (because you're in a shared hosting environment) the most information you're going to get is from the Application_End event and by asking the HttpRuntime (via reflection) for the values of one or two private members that are sadly not exposed publicly.如果无法访问事件日志(因为您处于共享托管环境中),您将获得的最多信息来自Application_End事件,并通过向HttpRuntime (通过反射)询问一两个私有成员的值遗憾的是没有公开曝光。

To do this add the following code to your Application_End event:为此,请将以下代码添加到您的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
}

If I shutdown or recycle my app's application pool I see the following:如果我关闭或回收我的应用程序的应用程序池,我会看到以下内容:

HostingEnvironment initiated shutdown
HostingEnvironment caused shutdown -    
   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()

That's probably about as good as it gets.这可能是最好的。

Update:更新:

I couldn't remember where I found this code but Drew helpfully reminded me it was from a Scott Guthrie blog post.我不记得我在哪里找到了这段代码,但 Drew 提醒我它来自 Scott Guthrie 的一篇博文。

There are some other private members that could be useful such as:还有一些其他私有成员可能有用,例如:

private ApplicationShutdownReason _shutdownReason;

You can examine these fields in .NET Reflector (if you still have a copy that isn't time-bombed) or one of the alternatives ( Open Source Alternatives to Reflector? ).您可以在 .NET Reflector(如果您仍有未定时炸弹的副本)或其中一种替代品( 反射器的开源替代品? )中检查这些字段。

Research - 1研究 - 1

Firstly I tried using System.Web.ProcessModelInfo.GetCurrentProcessInfo() and System.Web.ProcessModelInfo.GetHistory(int) .首先我尝试使用System.Web.ProcessModelInfo.GetCurrentProcessInfo()System.Web.ProcessModelInfo.GetHistory(int) The results of these methods return info such as the PID, start time, age, status, and peak memory usage.这些方法的结果返回信息,例如 PID、开始时间、年龄、状态和峰值内存使用情况。 Unfortunately these were unavailable in my hosting environment:不幸的是,这些在我的托管环境中不可用:

HttpException 0x80004005 - Process metrics are available only when the ASP.NET process model is enabled. HttpException 0x80004005 - 进程指标仅在启用 ASP.NET 进程模型时可用。 When running on versions of IIS 6 or newer in worker process isolation mode, this feature is not supported.在工作进程隔离模式下在 IIS 6 或更高版本上运行时,不支持此功能。

This approach might work for others though, so if you're in this situation, give it a shot.不过,这种方法可能适用于其他人,因此如果您处于这种情况,请试一试。

Research - 2研究 - 2

The property System.Web.Hosting.HostingEnvironment.ShutdownReason is an enum with lots of values, but unfortunately all the cases I outline in my question are bundled into a single enum value:属性System.Web.Hosting.HostingEnvironment.ShutdownReason是一个具有很多值的枚举,但不幸的是,我在问题中概述的所有情况都捆绑到一个枚举值中:

ApplicationShutdownReason.HostingEnvironment : The hosting environment shut down the application domain. ApplicationShutdownReason.HostingEnvironment :托管环境关闭应用程序域。

Research - 3研究 - 3

ScottGu has an approach on his blog (which is the same code Kev posted ) that uses reflection to access internal state of the HttpApplication . ScottGu 在他的博客上有一种方法(与Kev 发布的代码相同),它使用反射来访问HttpApplication内部状态。 Unfortunately in this case it only reports the same detail as #2 above:不幸的是,在这种情况下,它只报告与上面 #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()

Below is good code find from https://mitchelsellers.com/blog/article/logging-asp-net-application-restarts下面是从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;
}

This is a really late answer, but I hope it can provide extra insight for those having similar problems (IIS 7.x or above).这是一个非常晚的答案,但我希望它可以为那些有类似问题(IIS 7.x 或更高版本)的人提供额外的见解。

1. Finding when application pool is starting to shutdown - the following code can be used to find out when the application pool start its shutdown. 1. 查找应用程序池何时开始关闭- 以下代码可用于查找应用程序池何时开始关闭。 Actual shutdown occurs in a maximum of Shutdown limit (seconds, default 90) after this event.实际关闭发生在此事件后的最大关闭限制(秒,默认为 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());
}

This helps to find the general reason and exactly when it was triggered.这有助于找到一般原因以及触发的确切时间。 In your case, I think HostingEnvironment is the value.在你的情况下,我认为HostingEnvironment是价值。 Unfortunately, the underlying cause is not unique.不幸的是,根本原因并不是唯一的。 It can be periodic recycle, recycle due to memory limit (most probable reason in OP's question), recycle due to fixed hour etc.它可以是定期回收,由于内存限制而回收(OP 问题中最可能的原因),由于固定时间回收等。

2. Finding the exact cause - one way to find out the exact cause is to search for it in the EventLog. 2. 查找确切原因-查找确切原因的一种方法是在 EventLog 中搜索。 If this is not accessible, it can be requested from the hosting provider by providing the following details to narrow their search.如果无法访问,可以通过提供以下详细信息向托管服务提供商请求以缩小搜索范围。

  • Exact time of shutdown initiation关机启动的确切时间
  • Event log filter:事件日志过滤器:
    • Event sources = WAS事件源 = WAS
    • Event level = Information事件级别 = 信息
    • Logged = custom range including exact time of shutdown +/- 1 minute or so记录 = 自定义范围,包括准确的关机时间 +/- 1 分钟左右

Event log should return more relevant information like the ones below:事件日志应返回更多相关信息,如下所示:

A worker process with process id of 'xxx' serving application pool 'xxx' has requested a recycle because it reached its scheduled recycle time.服务应用程序池“xxx”的进程 ID 为“xxx”的工作进程已请求回收,因为它已达到预定的回收时间。


A worker process with process id of 'xxx' serving application pool 'xxx' has requested a recycle because it reached its virtual memory limit.服务应用程序池“xxx”的进程 ID 为“xxx”的工作进程已请求回收,因为它已达到其虚拟内存限制。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM