如何防止 ASP.NET 应用程序在修改 web.config 后重新启动?

38

我正在使用ApplicationHost.CreateApplicationHost方法托管ASP.NET运行时。当应用程序在运行时修改web.config文件时,会看到大量的ThreadAbortException异常被抛出。这是在我的应用程序崩溃之前发生的。我假设这是因为运行时检测到配置更改并想要重新启动。

这对我们来说不是一个真正支持的场景,因此我希望我可以关闭自动重新加载。

有人知道如何做吗?


2
<compilation debug="true" numRecompilesBeforeAppRestart="15000"> - MartinHN
5个回答

35

实际上,前两个答案是不正确的。防止这种回收发生是可能的,而且非常容易,自至少IIS6以来就有了这个功能。

方法1(系统范围)

更改HKLM\SOFTWARE\Wow6432Node\Microsoft\ASP.NET\FCNModeDWORD注册表设置为值1,这将禁用所有文件更改通知。

不要被位置所迷惑:Wow6432Node在这种情况下对您的Web应用程序的位数没有影响。

方法2(.NET 4.5+)

如果您使用的是.NET 4.5,则现在可以在每个站点上禁用此功能,只需在您的web.config中使用以下内容:

<httpRuntime fcnMode="Disabled"/> 

方法三(IIS6+)

最后,也是自IIS6起存在的设置,名为DisallowRotationOnConfigChange的设置仅适用于应用程序池(至少我认为MSDN上的文本是这样说的,但我还没有测试过)。将其设置为true,并且对应用程序池的配置更改将不会立即导致重新启动。

此设置也可以从应用程序池的高级设置中设置:

Disable Recycling for Configuration Change

方法4(ASP.NET 1.0和1.1)

对于使用ASP.NET 1.0或1.1的(旧)网站,存在一个已确认的错误(bug),可能会导致文件更改时的快速和重复的重新加载。当时的解决方法类似于主问题下MartinHN建议的方式,即在您的web.config中添加以下内容:

<compilation 
   debug="false"
   defaultLanguage="vb"
   numRecompilesBeforeAppRestart="5000">

这并不会禁用回收,但只有在重新编译发生了5000次之后才会执行。这个数字是否有用取决于您的应用程序大小。Microsoft没有明确说明什么是重新编译。然而,默认值是15。
另外提一下:无论是哪个版本的.NET或Windows,我们发现当应用程序从共享中运行并在负载平衡环境中使用时,站点会持续回收。唯一解决方法是将FNCMode设置添加到注册表中(但现在有更精细的选项可供选择)。

1
我认为现在使用方法2可能是正确的选择。我不确定方法3,它是否可以通过 web.config 进行设置并且是否可以防止所有文件更改时重新加载。请注意,此应用程序不是通过 IIS 运行的,而是由 WPF 应用程序托管的 ASP.NET AppDomain。方法1并不是一个真正的选择,因为它会影响其他应用程序。 - Jacob Stanley
@JacobStanley:如果您使用新的AppDomain并在同一AppPool中运行它,则无论您是通过更改web.config中的fcnMode(方法2)还是在AppPool设置中设置它(方法3),都不太重要。同一站点的web.config可以被多个AppPools使用,尽管通常情况下同一AppPool用于多个站点,在这种情况下,最好使用具有fcnMode的方法2(更好的每个站点粒度)。请注意,您甚至可以通过在其中添加web.config来为一个站点中的单独目录设置fcnMode - Abel
@Abel,实际上DisallowRotationOnConfigChange设置仅适用于应用程序池的设置更改,而不适用于web.config中的更改。有关更多信息,请参见此线程 - Azimuth
@azi,这就是我在文本中解释的内容,我写道“仅作为应用程序池的设置”。 - Abel
1
我已经在Windows Server 2016上尝试了方法1、2和3,但即使将所有3种方法组合起来,对web.config或applicationHost.config进行任何更改都会导致应用程序重启。您能否确认这些方法是否适用于Windows Server 2016? - Ron
显示剩余2条评论

28
据我所知,没有办法禁用此行为,对webconfig的更改会强制应用程序重新启动。
更新:实际上是可能的,有许多方法,这些方法都有很好的文档记录,如在这个答案中所解释的*。
原始答案:
这里有一个类似的问题,供参考。我找到了一些额外的信息,可能会有帮助。
引用:
配置更改导致应用程序域重新启动。Web.config文件中的配置设置的更改间接地导致应用程序域重新启动。这种行为是按设计进行的。您可以选择使用configSource属性来引用外部配置文件,在进行更改时不会导致重新启动。有关更多信息,请参见
Section元素继承的一般属性中的configSource。
来自这篇MSDN文章 *免责声明:我写了另一个答案,通常不会自我引用,但认为它足够相关,因此在这里链接,因为在此帖子8年后,它真的非常不同:通过单击IIS前端,解决方案非常容易,并且自ASP.NET 1.0以来存在解决方法。

1
请纠正我,但我认为外部配置文件也会重新启动应用程序? - John B
1
好的,我猜那篇MSDN文章是说这些不会导致应用程序重新启动,但这是否意味着配置设置不能立即加载? - John B
1
好的,我刚刚进行了一些调查,在我的ASP.NET 3.5网站上,应用程序池没有重新启动,并且更新的设置值已经反映在网页中。然而,我仍然觉得我曾经看到过一个Web应用程序,当外部配置文件被更改时会重置。也许那个应用程序有特殊之处,或者可能是较旧版本的ASP.NET。 - John B
非常容易且方便地更改此默认行为(自Windows 2003以来已经如此),请参见下面的我的答案 - Abel
使用 .Net 4+,我们无法通过修改外部配置文件来始终重新启动 AppDomain。 - OverMars

20

我在同一问题上遇到了更大的问题 - 在AppDomain基目录中更改任何文件或子文件夹都会导致托管环境关闭。对于我们的应用程序来说,这是一个相当大的问题,因为我们在同一AppDomain中运行一个WPF UI,而且我们不能在不干扰用户的情况下重新启动它。

我真的想避免必须为基于Web的应用程序运行单独的AppDomain,所以我使用Reflector进行了一些挖掘。 我发现罪魁祸首是内部类FileChangesMonitor

因此,我写了一个非常可怕的反射hack来解决这个问题。 我认为我应该将其发布在这里,作为可能的解决方案供其他遇到同样问题的人参考。 您只需要调用HttpInternals.StopFileMonitoring()来禁用文件/文件夹更改时的关闭。

internal static class HttpInternals
{
    private static readonly FieldInfo s_TheRuntime = typeof(HttpRuntime).GetField("_theRuntime", BindingFlags.NonPublic | BindingFlags.Static);

    private static readonly FieldInfo s_FileChangesMonitor = typeof(HttpRuntime).GetField("_fcm", BindingFlags.NonPublic | BindingFlags.Instance);
    private static readonly MethodInfo s_FileChangesMonitorStop = s_FileChangesMonitor.FieldType.GetMethod("Stop", BindingFlags.NonPublic | BindingFlags.Instance);

    private static object HttpRuntime
    {
        get
        {
            return s_TheRuntime.GetValue(null);
        }
    }

    private static object FileChangesMonitor
    {
        get
        {
            return s_FileChangesMonitor.GetValue(HttpRuntime);
        }
    }

    public static void StopFileMonitoring()
    {
        s_FileChangesMonitorStop.Invoke(FileChangesMonitor, null);
    }
}

2
但是appDomains旨在反映应用程序域 - 这在WPF和Web之间显然是不同的。您正在以一种不符合其预期用途的方式使用该对象。 - annakata
这可能会更加复杂 :)如果您熟悉Reflector,可以看到System.Web.FileChangesMonitor.Stop()做了什么,然后尝试反转它。看起来ASP.NET通常只在关闭时使用Stop()方法。FileChangesMonitor有一些称为StartListeningToLocalResourceDirectory和其他Start*方法的方法看起来很有前途。如果您能弄清楚HttpRuntime类如何初始化FileChangesMonitor,那么您就走上了正确的道路。听起来甚至比我最初做的还要可疑! :) - Jacob Stanley
@Jacob Stanley:是的,这很危险。我正在尝试更新一个正在运行的预编译网站的单个函数。使用Reflector和Reflexil来完成 :) - AareP
虽然似乎在运行时更新方法还不可能。有一个名为PrepareMethod()的方法用于编译IL代码,但没有用于重新编译的方法。因此,一旦调用该方法,替换其IL代码将无效。 - AareP
微软已经完全放弃了AppDomains;它们甚至在.NET Core/5/6中也不受支持,因为它们增加了不必要的复杂性。现在基本上就是“只需使用不同的进程”。此外,如果您正在使用像SignalFx这样的跟踪库,它会钩入CLR,他们建议仍然使用单个AppDomain,因为任何类型的进程内回收(例如当web.config文件更改时)都将导致尝试以引起共享冲突的方式加载域中立程序集并崩溃整个进程。最好避免使用AppDomains。 - Triynko
显示剩余2条评论

11
一个解决方案是在web.config的节中添加以下元素:
<httpRuntime
    waitChangeNotification="315360000"
    maxWaitChangeNotification="315360000"
/>

但是,遗憾的是,在IIS7中这并不完全适用:http://forums.iis.net/t/1149344.aspx?IIS7+no+longer+honors+waitChangeNotification+and+maxWaitChangeNotification(虽然我刚意识到发帖人没有使用IIS,所以在这种情况下没问题) - Tao
我发现这个答案非常有用,因为当尝试一次性更新10个文件时,您不希望进行10次应用程序池重启。这个设置将允许您将一堆更新的文件合并成一个重置。当然,需要较小的数字才能使其有用。 - Brain2000
2
实际上是这样的。但仅适用于BIN文件夹。web.config仍会导致立即回收:http://beweb.pbworks.com/w/page/30073098/Prevent%20app%20restarts%20and%20site%20downtime%20when%20deploying%20files - Brain2000

0

正如jfburdet所提到的,解决方案是使用waitChangeNotification和maxWaitChangeNotification。

话虽如此,您应该知道,如果在混合模式下运行ASP.NET,则它们不适用于IIS 7:http://forums.iis.net/t/1149344.aspx


实际上它确实有效。但仅适用于BIN文件夹,web.config仍会导致立即回收。请在此文章的三分之一处检查:http://beweb.pbworks.com/w/page/30073098/Prevent%20app%20restarts%20and%20site%20downtime%20when%20deploying%20files - Brain2000

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接