IIS AlwaysRunning 深入研究 - 幽靈排程與自動啟動行為
0 | 2,723 |
之前研究過確保網站永遠處於執行狀態的 IIS 設定方式,最近遇上網站停用但網站背景排程照跑的靈異事件,發現事情跟我想的不一樣,自己對 IIS 站台 Process 模式及 AlwaysRunning 行為有些誤解,寫篇筆記備忘。
我們都知道 IIS 管理員站台有組控制鈕,可以重新啟動、啟動或停止站台。
大家想像按下去的動作是什麼?重啟或切換 AppPool 狀態?並不是! 這也是這次我誤解的主要來源。
.NET 在一個 Process 裡可建立多個記憶體空間和資源獨立的 AppDomain,達到比另建 Process 省資源又兼顧安全及穩定性隔離的效果。(參考:VITO の 學習筆記:什麼是應用程式定義域 ( application domain ))。IIS 7+ 的做法是在第一次收到 ASP.NET 網頁要求時,由 IIS 受控引擎模組在記憶體建立 AppDomain,由 AppDomain 處理 aspx 頁面要求。(詳情可參考 MS Learn 文件)
為了實地驗證,我做了一個簡單的 ASP.NET Web Site Project 網站,並設好 AlwaysRunning、Preload Enabled:
Global.asax 會啟動一個 Task 跑背景排程,每一秒寫入一筆 Log,另外,在站啟動及結束時也會寫 Log。Log 訊息前方則會加上 Process ID # AppDomain ID 方便觀察: (延伸閱讀:新時代 .NET ThreadPool 程式寫法以及為什麼你該用力 async/await)
<%@ Application Language="C#" %>
<%@ Import Namespace="System.Threading" %>
<%@ Import Namespace="System.Threading.Tasks" %>
<%@ Import Namespace="System.Diagnostics" %>
<script RunAt="server">
CancellationTokenSource cts;
void Log(string msg)
{
System.IO.File.AppendAllText("D:\\Logs\\trace.txt",
Process.GetCurrentProcess().Id +
"#" + AppDomain.CurrentDomain.Id +
" - " + msg + "\n");
}
void Application_Start(object sender, EventArgs e)
{
Log("Application Start");
cts = new CancellationTokenSource();
Task.Run(async () =>
{
while (!cts.Token.IsCancellationRequested)
{
Log(DateTime.Now.ToString("HH:mm:ss"));
await Task.Delay(1000, cts.Token);
}
});
}
void Application_End(object sender, EventArgs e)
{
cts.Cancel();
Log("Application End");
}
</script>
有了這個小實驗,我的疑惑很快獲得解答:
- 啟動停止中的站台時會自動啟動網站嗎?
答案是不會, AlwaysRunning、Preload Enabled 自動啟動網站只適用電腦重開或 AppPool 重啟時。如下面展示,按下 Start 網站沒開始寫 Log,得等呼叫http://localhost:8000/
後才開始:
如前面所提,站台需等到第一個請求進來時才啟動 AppDoamin。而由 Process ID / AppDomain ID 可證明站台重啟後仍是同一個 Process,只有換 AppDomain。
同理,更新 bin、web.config 也會造成 AppDomain 停止,之後需發出 HTTP 請求才能啟動網站。(註:web.config 的 initializationPage 設定是在網站啟動後自動呼叫,無法用來觸發停止中的站台)
以下展示在 bin 寫入檔案讓網站停止,停止後用 PowerShell 發送 HTTP 請求後才被喚醒另建 AppDomain 執行:(AppDomain Id 由 4 變 5)
- 那 IISEREST 或重啟 W3WC 服務後網站會自動啟動嗎?
AppPool 重啟時,不需 HTTP 請求便可啟動 AlwaysRunning + Preload 網站,實驗可觀察到重啟後 ProcessID 不同。
- 「站台已停用,網站背景程序卻還在執行」的靈異現象是怎麼發生的?
如第 2 點所說,設定 AlwaysRunning 的站台會在機器重啟或 IISRESET 時自動啟動,即便站台已被停用。簡單解釋是停用站台是停掉目前執行中的 AppDoamin 並拒絕接收 HTTP 請求,但 AlwaysRunning + Preload Enabled 會在 AppPool 啟動時建立 AppDomain 執行,造成這次的靈異現象。
歸納結論:
- IIS 站台是以 AppDomain 方式執行,一個 AppPool Process 可有多個 AppDomain,IIS 管理的站台啟動、停止、重啟功能控制的是 AppDomain,bin 或 web.config 異動也會造成 AppDomain 結束
- AlwaysRunning + Preload Enabled 適用重新開機、AppPool 啟動、IISREST 後自動啟動網站,IIS 管理員重啟站台後需對網站發動請求才會建立 AppDomain
- 站台停用狀態下,AlwaysRunning + Preload Enabled 網站會在 AppPool 啟動時自動啟動。若不想程式被誤執行,可將 AppPool 也停用 (註:IIS 管理員設定停止的 AppPool 及站台,重新開機後仍會維持停止狀態)
Explanation of AppDomain operation design in IIS sites and how AlwaysRunning and Preload Enabled settings work, demonstrated by experiments.
Comments
Be the first to post a comment