private void Application_End()
{
log.Warn($"The application is shutting down because of '{HostingEnvironment.ShutdownReason}'.");
TelemetryConfiguration.Active.TelemetryChannel.Flush();
// Server Channel flush is async, wait a little while and hope for the best
Thread.Sleep(TimeSpan.FromSeconds(2));
}
你将最终得到 "The application is shutting down because of 'ConfigurationChange'." 或 "The application is shutting down because of 'HostingEnvironment'." ,但它并没有在主机级别上进行't really tell you what' .
要考虑的第三件事是你是否正在使用App Service Local Cache Option . 简而言之,这是一个选项,其中App Service将复制运行的应用程序's files to the local storage of the instances that it'而不是网络共享,如果您的应用程序不关心它是否会丢失写入本地文件系统的更改,那么这是一个很好的选择 . 它可以加快I / O性能(这很重要,因为请记住,App Service runs on potatoes)并消除由网络共享上的任何维护引起的重新启动 . 但是,那里当本地缓存准备就绪时,关于App Service 's infrastructure upgrades that is poorly documented and you need to be aware of. Specifically, the Local Cache option is initiated in the background in a separate app domain after the first request, and then you'切换到app域是一个特殊的微妙之处 . 这意味着App Service将针对您的站点发出预热请求,获得成功响应,为该实例指定流量,但是(哎呀!)现在Local Cache正在后台研磨I / O,如果您有很多站点在这个例子中,你知道这种情况正在发生,它在日志中看起来很怪异,因为它就好像你的应用程序在同一个实例上启动了两次(因为它是) . 解决方案是遵循此Jet blog post并创建一个应用程序初始化预热页面来监视环境变量,该变量告诉您本地缓存何时就绪 . 这样,您可以强制App Service延迟将您引导至新实例,直到完全准备好本地缓存 . 这是我用来确保我可以与数据库通信的一个:
public class WarmupHandler : IHttpHandler
{
public bool IsReusable
{
get
{
return false;
}
}
public ISession Session
{
get;
set;
}
public void ProcessRequest(HttpContext context)
{
if (context == null)
{
throw new ArgumentNullException("context");
}
var request = context.Request;
var response = context.Response;
var localCacheVariable = Environment.GetEnvironmentVariable("WEBSITE_LOCAL_CACHE_OPTION");
var localCacheReadyVariable = Environment.GetEnvironmentVariable("WEBSITE_LOCALCACHE_READY");
var databaseReady = true;
try
{
using (var transaction = this.Session.BeginTransaction())
{
var query = this.Session.QueryOver<User>()
.Take(1)
.SingleOrDefault<User>();
transaction.Commit();
}
}
catch
{
databaseReady = false;
}
var result = new
{
databaseReady,
machineName = Environment.MachineName,
localCacheEnabled = "Always".Equals(localCacheVariable, StringComparison.OrdinalIgnoreCase),
localCacheReady = "True".Equals(localCacheReadyVariable, StringComparison.OrdinalIgnoreCase),
};
response.ContentType = "application/json";
var warm = result.databaseReady && (!result.localCacheEnabled || result.localCacheReady);
response.StatusCode = warm ? (int)HttpStatusCode.OK : (int)HttpStatusCode.ServiceUnavailable;
var serializer = new JsonSerializer();
serializer.Serialize(response.Output, result);
}
}
要考虑的第五件事,这主要是我的问题一直是,你是否正在使用启用 AlwaysOn 选项的临时插槽 . AlwaysOn 选项的工作原理是每隔一分钟左右ping一次您的网站,以确保它将其旋转下来 . 令人费解的是,this isn't a sticky setting,所以你可能已经在你的制作和舞台插槽上打开了 AlwaysOn 所以你不知道发生了什么:假设你在一个实例上托管了7个站点,每个站点都有自己的临时插槽,所有启用了 AlwaysOn 的东西 . App Service会对您的7个 生产环境 插槽执行预热和应用程序初始化,并在重定向流量之前尽职地等待它们成功响应 . But it doesn't do this for the staging slots. 因此它将流量引导到新实例,但随后在分段插槽中1-2分钟后启动 AlwaysOn ,所以现在又有7个站点同时启动 . 请记住,App Service runs on potatoes,因此所有这些额外的I / O同时发生会破坏您的 生产环境 槽的性能,并将被视为停机时间 .
解决方案是在暂存插槽上保持 AlwaysOn 关闭,以便在基础结构更新后不会被这种同时发生的I / O狂热所困扰 . 如果您通过PowerShell使用交换脚本,维护此"Off in staging, On in production"令人惊讶的冗长:
1 回答
因此,似乎答案就是“不,你真的不知道为什么,你可以推断它确实存在 . ”
我的意思是,您可以添加一些Application Insights日志记录
你将最终得到
"The application is shutting down because of 'ConfigurationChange'."
或"The application is shutting down because of 'HostingEnvironment'."
,但它并没有在主机级别上进行't really tell you what' .我需要接受的是App Service会不时重新启动,并问自己为什么要关心 . App Service应该足够聪明,等待应用程序池在向其发送请求之前进行预热(例如重叠的回收) . 然而,我的应用程序在回收后会在CPU处理1-2分钟 .
我花了一段时间才弄清楚,但罪魁祸首是我的所有应用都有重写规则从HTTP重定向到HTTPS . 这不适用于Application Initialization模块:它向root发送请求,并且它从URL Rewrite模块获得301重定向,并且ASP.NET管道根本没有被击中,努力工作不是实际上完成了 . App Service / IIS然后认为工作进程已准备就绪,然后向其发送流量 . 但第一个“真实”请求实际上是在301重定向到HTTPS URL之后,而bam!那个用户遇到了冷启动的痛苦 .
I added a rewrite rule described here免除Application Initialization模块需要HTTPS,因此当它到达站点的根目录时,它实际上会触发页面加载,从而触发整个管道:
这是将旧应用程序迁移到Azure的日记中的众多条目之一 - 事实证明,当很少有重新启动的传统VM上运行某些东西时,你可以逃脱很多事情,但它需要一些TLC来解决这个问题 . 在 Cloud 中迁移到我们勇敢的新世界时扭结....
UPDATE 10/27/2017: 自撰写本文以来,Azure已在"Diagnose and solve problems"下添加了一个新工具 . 单击"Web App Restarted",它会告诉您原因,通常是因为存储延迟或基础架构升级 . 上面的内容仍然存在,因为当转移到Azure App Service时,最好的方法是你真的只需要让你的应用程序适应随机重启 .
UPDATE 2/11/2018: 将几个遗留系统迁移到中型应用服务计划的单个实例(具有大量CPU和内存开销)之后,我遇到了一个令人烦恼的问题,即我从暂存插槽部署无缝,但每当我被启动到由于Azure基础架构维护的新主机,一切都会因停机2-3分钟而失控 . 我正在疯狂地试图弄清楚为什么会发生这种情况,因为App Service应该等到它从你的应用程序收到成功的响应,然后才能启动你到新的主机 .
我对此非常沮丧,因此我准备将App Service归类为企业垃圾并返回IaaS虚拟机 .
事实证明这是多个问题,我怀疑其他人会在将他们自己的野兽遗留的ASP.NET应用程序移植到App Service时遇到它们,所以我想我会在这里完成它们 .
要检查的第一件事是你实际上在
Application_Start
中做了真正的工作 . 例如,我正在使用NHibernate,虽然在很多方面很擅长加载它的配置,所以我确保在Application_Start
期间实际创建SessionFactory
以确保完成了艰苦的工作 .如上所述,要检查的第二件事是你要进行热身检查 . 如上所述,您可以从重写规则中排除热身检查 . 或者,在我最初编写该工作的时候,App Service添加了一个HTTPS Only标志,允许您在负载均衡器而不是web.config文件中执行HTTPS重定向 . 既然它必须考虑它,所以我建议使用HTTPS Only标志作为前进的方法 .
要考虑的第三件事是你是否正在使用App Service Local Cache Option . 简而言之,这是一个选项,其中App Service将复制运行的应用程序's files to the local storage of the instances that it'而不是网络共享,如果您的应用程序不关心它是否会丢失写入本地文件系统的更改,那么这是一个很好的选择 . 它可以加快I / O性能(这很重要,因为请记住,App Service runs on potatoes)并消除由网络共享上的任何维护引起的重新启动 . 但是,那里当本地缓存准备就绪时,关于App Service 's infrastructure upgrades that is poorly documented and you need to be aware of. Specifically, the Local Cache option is initiated in the background in a separate app domain after the first request, and then you'切换到app域是一个特殊的微妙之处 . 这意味着App Service将针对您的站点发出预热请求,获得成功响应,为该实例指定流量,但是(哎呀!)现在Local Cache正在后台研磨I / O,如果您有很多站点在这个例子中,你知道这种情况正在发生,它在日志中看起来很怪异,因为它就好像你的应用程序在同一个实例上启动了两次(因为它是) . 解决方案是遵循此Jet blog post并创建一个应用程序初始化预热页面来监视环境变量,该变量告诉您本地缓存何时就绪 . 这样,您可以强制App Service延迟将您引导至新实例,直到完全准备好本地缓存 . 这是我用来确保我可以与数据库通信的一个:
还记得映射路由并添加应用程序初始化
web.config
:要考虑的第四件事是,有时App Service会因为看似垃圾的原因重新启动你的应用程序 . 似乎将
fcnMode
属性设置为Disabled
可以提供帮助;如果有人在服务器上处理配置文件或代码,它会阻止运行时重新启动应用程序 . 如果你打扰了你 . 但是,如果您希望能够使用FTP并使用文件进行操作并查看 生产环境 中反映的更改,则不要使用此选项:要考虑的第五件事,这主要是我的问题一直是,你是否正在使用启用
AlwaysOn
选项的临时插槽 .AlwaysOn
选项的工作原理是每隔一分钟左右ping一次您的网站,以确保它将其旋转下来 . 令人费解的是,this isn't a sticky setting,所以你可能已经在你的制作和舞台插槽上打开了AlwaysOn
所以你不知道发生了什么:假设你在一个实例上托管了7个站点,每个站点都有自己的临时插槽,所有启用了AlwaysOn
的东西 . App Service会对您的7个 生产环境 插槽执行预热和应用程序初始化,并在重定向流量之前尽职地等待它们成功响应 . But it doesn't do this for the staging slots. 因此它将流量引导到新实例,但随后在分段插槽中1-2分钟后启动AlwaysOn
,所以现在又有7个站点同时启动 . 请记住,App Service runs on potatoes,因此所有这些额外的I / O同时发生会破坏您的 生产环境 槽的性能,并将被视为停机时间 .解决方案是在暂存插槽上保持
AlwaysOn
关闭,以便在基础结构更新后不会被这种同时发生的I / O狂热所困扰 . 如果您通过PowerShell使用交换脚本,维护此"Off in staging, On in production"令人惊讶的冗长:此脚本将暂存插槽设置为启用
AlwaysOn
,执行交换以使暂存现在正在 生产环境 ,然后将暂存插槽设置为关闭AlwaysOn
,这样在基础结构升级后它不会破坏 .一旦你开始工作,拥有一个能够为你处理安全更新和硬件故障的PaaS确实很不错 . 但在实践中实现比营销材料可能提出的要困难一些 . 希望这有助于某人 .