Как узнать, почему пул приложений IIS перерабатывается
Справочная информация:
Я развернул приложение ASP.NET MVC 3, которое работает на моей машине, в поставщик общедоступного хостинга, и я обнаружил некоторые проблемы, которые, как представляется, связаны с пул приложений перерабатывается. Хост настроил рециркуляцию, чтобы произойти в любом из этих обстоятельств:
- Использование памяти превышает 200 МБ
- Использование ЦП превышает 75% (предположительно в течение длительного периода)
- 20 минут простоя
Ограничения более смягчены на моей машине разработки, поэтому я не видел рециркуляции, как это во время разработки. У меня нет доступа администратора к общему ящику (понятно), поэтому я не могу прочитать журнал событий, чтобы узнать, почему происходит эта рециркуляция.
Вопрос:
Есть ли способ узнать, почему мое приложение было переработано (например, в Application_End
), чтобы я мог его зарегистрировать, чтобы помочь отладке?
Ответы
Ответ 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 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()
Это, вероятно, так же хорошо, как и получается.
Update:
Я не мог вспомнить, где я нашел этот код, но Дрю помог мне вспомнить, что это было в блоге Скотта Гатри.
Есть и другие частные члены, которые могут быть полезны, например:
private ApplicationShutdownReason _shutdownReason;
Вы можете просмотреть эти поля в .NET Reflector (если у вас все еще есть копия, которая не бомбардирована) или один из альтернатив (Open Source Alternatives to Reflector?).
Ответ 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 есть подход в своем блоге (который является тем же кодом Kev опубликовал), который использует отражение для доступа к внутреннему состоянию 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
Ниже приведен хороший код из http://mitchelsellers.com/blogs/2007/03/15/logging-aspnet-application-restarts.aspx
// 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
- это значение. К сожалению, основная причина не уникальна. Это может быть периодическая рециркуляция, переработка из-за ограничения памяти (наиболее вероятная причина в вопросе ОП), рециркуляция из-за фиксированного часа и т.д.
2. Поиск точной причины - один из способов выяснить точную причину - найти его в EventLog. Если это не доступно, его можно запросить у хостинг-провайдера, указав следующие данные, чтобы сузить их поиск.
- Точное время инициализации выключения
- Фильтр журнала событий:
- Источники событий = WAS
- Уровень события = информация
- Записан = пользовательский диапазон, включая точное время выключения +/- 1 минута или около того
Журнал событий должен возвращать более релевантную информацию, подобную приведенной ниже:
Рабочий процесс с идентификатором процесса в пуле приложений для приложения "xxx" "xxx" запросил переработку, потому что он достиг своего запланированное время утилизации.
Рабочий процесс с идентификатором процесса в пуле приложений для приложения "xxx" "xxx" запросил переработку, потому что он достиг своей виртуальной памяти предел.