子目錄 web.config 修改後會立即生效?會導致 AppPool 重啟嗎?
2 |
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 重啟嗎?
被同事問到這個問題,驚覺原來我也不知道答案,趕緊做實驗補上知識缺口。
先說結論:
- /wwwroot/subdir/web.config 會觸發 Web Application 重啟,設定將立即生效
- 我們平常說的 Web Application 重啟,其實可分為兩種狀況:Application Pool 回收或 Application Domain 重建
- 修改 web.config 會導致 AppDomain 重建但不會影響 AppPool,故文章標題所說的「更新 dll 及修改 web.config 會重啟 AppPool」的說法是錯的
- 修改子目錄的 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 疑惑。
【延伸閱讀】
- Difference between Application Pool (AppPool) and Application Domain (AppDomain). by Kiran K Kadam
- App Pool vs. App Domain by Huan-Lin 學習筆記
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 重建的案例很有趣,看能否提供重現問題的步驟。