ASP.NET/IIS 用了 20 年 (ASP.NET 1.0 誕生於 2002 年 1 月,到今年 20 歲了),仍有不少我不確定答案的疑惑。

我們都知道,同一網站的 web.config 有繼承關係,網站根目錄 /wwwroot/web.config 設定套用全站,/wwwroot/subdir/web.config 的設定只對 /wwwroot/subdir/ 的 ASP.NET 程式有效。然後,我們也知道,除了更新 bin 目錄的 DLL,修改 根目錄的 web.config 也會導致 Web Appliation 重啟,更可用更名 web.config 成 web.config_ 再馬上改回來的技巧,不需管理者權限便能重啟 Web Application。(註:還有一些其他狀況也會導致重啟,例如:刪除 App_Data 下的子目錄
延伸閱讀:Why does an application domain recycle? by Debugger Lady

那,修改 /wwwroot/subdir/web.config 要重啟 Web Application 才會生效嗎?如果會馬上生效,它會導致整個網站的 Web Application 重啟嗎?

被同事問到這個問題,驚覺原來我也不知道答案,趕緊做實驗補上知識缺口。

先說結論:

  1. /wwwroot/subdir/web.config 會觸發 Web Application 重啟,設定將立即生效
  2. 我們平常說的 Web Application 重啟,其實可分為兩種狀況:Application Pool 回收或 Application Domain 重建
  3. 修改 web.config 會導致 AppDomain 重建但不會影響 AppPool,故文章標題所說的「更新 dll 及修改 web.config 會重啟 AppPool」的說法是錯的
  4. 修改子目錄的 web.config 也會導致 AppDomain 重建,修改將立即生效,但 Session、Cache 也會因此遺失

有兩個術語要先釐清 - AppPool 與 AppDomain。

AppPool 大家應該不陌生,它是 IIS 的管理站台程序的基本單位,不只 ASP.NET,在 IIS 跑 PHP、Python 也會用到它;而 ASP.NET 在 AppPool 執行時,會被包在所稱 AppDomain 的隔離容器中,好處是各 AppDomain 彼此獨立,有自己的記憶體定址、安全存取等級(Security Access Level)、組件版本組合,不會打架;當某個 AppDomain 崩潰時不會影響其他 AppDomain 或底層的 Process;各 AppDomain 間雖不能直接呼叫,但可以透過 Proxy 溝通,由於 AppDomain 身處同一個 Process,溝通成本比跨 Process 低。

每個 AppPool 是一個 w3wp Process,多個 Web Application 可以共用 AppPool,但每個 Web Application 會跑在自己的 AppDomain 裡。

ASP.NET Session、靜態屬性值、記憶體快取的生命週期跟著 AppDomain,當 bin/*.dll 更新、web.config 被修改(不管根目錄或子目錄的 web.config),AppDomain 便會被重建。換言之 Session、記憶體快取將會消失,基本上可視為 Web Application 已重啟。

接著,我們就用實驗來驗證上述理論。

實驗一 多個 Web Application 共用 AppPool 時,各自跑在自己的 AppDoamin 中

AppDomainInfo.aspx 內容如下,放在 ℎttp://localhost/aspnet 及 ℎttp://localhost:8511/ 兩個 Web Application 執行。

<%@ Page Language="C#"%>
<%@ Import Namespace="System.Diagnostics" %>
<%@ Import Namespace="System.Runtime" %>
<script runat="server">
void Page_Load(object sender, EventArgs e) 
{
	Response.ContentType = "text/plain";
	Response.Write("Process Id = " + Process.GetCurrentProcess().Id.ToString());
	Response.Write("\nAppDomain Id = " + AppDomain.CurrentDomain.Id.ToString());
}
</script>

我讓 ℎttp://localhost:8511/ 跟 ℎttp://localhost/aspnet 共用名為 SharedAppPool 的應用程式集區:

同時執行兩邊的 AppDomainInfo.aspx,可看到二者的 Process Id 與 SharedAppPool w3wp.exe 的 Process ID 49784 相同,而 AppDomain ID 則一個是 2 一個是 3。

實驗二 修改 web.config 會重建 AppDomain 而非 AppPool;回收 AppPool Process Id 才會變

修改儲存 web.config 後 AppDomain ID 由 2 變 3,但 Process Id 不變;回收 AppPool 後,Process Id 改變,AppDomain 也重建。

實驗三 AppDomain 重建時,Session 會消失,靜態屬性也會重新初始化

在 App_Code 寫了一個 App_Code\StaticClass.cs 觀察靜態屬性值:

using System;

public class StaticClass 
{
	static DateTime CreatedTime = DateTime.Now;
	public static string Duration =>
		(DateTime.Now - CreatedTime).TotalMilliseconds.ToString("n0") + "ms";
}

configtest/default.aspx 觀察 Session、StaticClass.Duration、Process Id、AppDomain Id 及 appSetting 值:

<%@ Page Language="C#"%>
<%@ Import Namespace="System.Diagnostics" %>
<%@ Import Namespace="System.Runtime" %>
<script runat="server">
protected string MySetting = System.Configuration.ConfigurationManager.AppSettings["MySetting"];
void Page_Load(object sender, EventArgs e) 
{
	if (Session["SessData"] == null) 
		Session["SessData"] = DateTime.Now.ToString("mm:ss.fff");
	Response.ContentType = "text/plain";
	Response.Write("Process Id = " + Process.GetCurrentProcess().Id.ToString());
	Response.Write("\nAppDomain Id = " + AppDomain.CurrentDomain.Id.ToString());
	Response.Write("\nApplication Age = " + StaticClass.Duration);
	Response.Write("\nSessData = " + Session["SessData"]);
	Response.Write("\nMySetting = " + MySetting);
}
</script>

如以下展示,修改 configtest 子目錄下的 web.config 也會導致 AppDomain 重建,重建後 Session 值改變,代表資料已消失,靜態屬性也被重新初始化。

做完實驗,理論得到印證,心裡也踏實了,再解掉一件 ASP.NET 疑惑。

【延伸閱讀】

Study of relationship between AppPool and AppDomain and how web.config affect AppDomain recreationg and session states.


Comments

# by ChrisTorng

我覺得要提醒一下,AppDomain 是 ASP.NET (.NET Framework) 時代的東西,在 ASP.NET Core (.NET Core) 中已經沒有了。我只記得有 InProcess (IIS) 跟 OutOfProcess (IIS (proxy to)→ Kestrel) 兩種類型。ASP.NET Core 部署到 IIS 下仍有 web.config,但是否遵守上文規則就不清楚。ASP.NET Core 是否仍有針對其他檔案的類似重啟規則? 另依以前的經驗,好像不是所有 web.config 內容的修改都會導致重啟? 期待後續解惑... :P

# by Jeffrey

to ChrisTorng,依我所知,ASP.NET Core 不再有「更新檔案觸發網站重啟」的機制,而是會出現檔案鎖定無法更新,要靠 app_offline.htm 這種解法 (https://blog.darkthread.net/blog/appoffline-msbuild-task/) (.NET 6 有個陰影複製(Shadow-Copying)功能,但仍在實驗階段)。 修改 web.config 但不觸發 AppDomain 重建的案例很有趣,看能否提供重現問題的步驟。

Post a comment