使用IIS應用程序初始化來保持ASP.NET應用程序的活動
https://weblog.west-wind.com/posts/2013/Oct/02/Use-IIS-Application-Initialization-for-keeping-ASPNET-Apps-alive
2013年10月2日•來自毛伊島,HI• 37條評論
最近幾個月我一直在使用Windows服務工作,而且事實證明,Windows服務是調試,部署,更新和維護的重要因素。獲取服務設置,調試和更新的過程是一項重要的工作,必須進行廣泛的文檔記錄和/或自動化。在構建服務的大多數項目中,人們最終爭先恐後地爭取用於管理的正確“流程”。
事實上,在最近的一篇博文中,我提到在最近的一個項目中,我一直在Windows服務中使用自托管SignalR,因為該應用程序實際上是一個“服務”,也需要發送大量的消息通過SignalR。但實際情況是,它也可能是一個IIS應用程序,其中包含在後臺運行的服務組件。無論你是哪種方式,它都是帶有內置Web服務器的Windows服務,或運行服務應用程序的IIS應用程序,它們都不遵循標準的服務或Web應用程序模板。
我個人更喜歡Web應用程序。在IIS內部運行我獲得了IIS平臺的所有好處,包括服務生命周期管理(崩潰和重啟),受控關閉,整個安全基礎設施,包括簡單的證書支持,代碼的熱插拔以及從中直接發布到IIS的能力在Visual Studio中輕松完成。
由於這些好處,我們開始從自托管服務轉移到ASP.NET Web應用程序。
ASP.NET即服務的缺失鏈接:自動加載
過去我曾想在ASP.NET中運行“類似服務”的應用程序,因為當你考慮它時,遠程控制Web應用程序要容易得多。服務被鎖定在啟動/停止操作中,但如果您在Web應用程序內部托管,則可以編寫自己的票證並從任何位置控制它。
現在,在IIS內部運行應用程序作為服務的棘手部分是如何啟動IIS和ASP.NET,以便即使在重置應用程序池後您的“服務”仍然存在。7年前,我通過使用網絡監視器(我自己的West Wind Web Monitor應用程序)偽造它。我正在運行以監視我的各種網站的正常運行時間,並讓監視器每隔20秒ping我的“服務”以有效地保持ASP。 NET存活或在重新加載後重新啟動它。我使用了一個簡單的調度程序類,它還包含一些“自我重新加載”的邏輯。Hacky肯定,但它可靠地工作。
幸運的是,一旦使用應用程序初始化模塊啟動應用程序池,就可以更輕松,更集成地讓IIS啟動ASP.NET。應用程序初始化模塊基本上允許您打開應用程序池和站點/ IIS應用程序上的預加載,這實際上是在啟動應用程序池後通過IIS管道發出請求。這意味著您的ASP.NET應用程序會立即生效,Application_Start會被激活,以確保您的應用始終保持運行狀態。所有其他功能,如應用程序池回收和空閑時間後自動關閉仍然有效,但IIS將始終立即重新啟動應用程序。
應用程序初始化入門
從IIS 8開始,Application Initialization是IIS功能集的一部分。對於IIS 7和7.5,可通過Web Platform Installer 單獨下載。使用IIS 8應用程序初始化是Windows或Windows Server Role Manager中的可選安裝組件:
這是一個可選組件,因此請確保明確選擇它。
應用程序初始化的IIS配置
需要在應用程序池和IIS應用程序級別上應用初始化。從IIS 8開始,可以通過IIS管理控制臺進行這些設置。
從應用程序池開始:
在這裏,你需要設置兩種自動開始其始終設置,並應設置為AlwaysRunning的STARTMODE。兩者都必須設置 - 默認情況下,Start Automatically標誌設置為true,並控制應用程序池本身的啟動,而啟動應用程序則需要Always Running標誌。如果沒有設置後一個標誌,則站點設置無效。
現在,在站點/應用程序級別,您可以指定站點是否應預加載:
將Preload Enabled標誌設置為true。
此時,ASP.NET應用程序應自動加載。如果你想要的只是讓你的網站自動啟動,這就是預加載網站所需的全部內容。
如果您想要更多地控制加載過程,可以在web.config文件中添加一些設置,以便在應用程序啟動時顯示靜態頁面。如果啟動速度很慢,這可能很有用,因此,當用戶擺弄拇指時,您可以顯示靜態HTML頁面,而不是顯示空白屏幕:
< system.webServer > < applicationInitialization remapManagedRequestsTo = “ Startup.htm ” skipManagedModules = “ true ” > < add initializationPage = “ ping.ashx ” /> </ applicationInitialization > </ system.webServer >
這允許您指定要在空運行中執行的頁面。IIS基本上偽造請求並將其直接推送到IIS管道而不會訪問網絡。您指定一個頁面,IIS將偽造對該頁面的請求,在這種情況下ping.ashx只返回一個簡單的OK字符串 - 即。快速的管道請求。應用程序池重新啟動後立即運行此請求,當此請求正在運行且您的應用程序正在預熱時,IIS可以顯示備用靜態頁面 - 上面的Startup.htm。因此,當您點擊網站上的鏈接時,您可以選擇顯示某種靜態狀態頁面,而不是向用戶顯示空的加載頁面,“我們會馬上回來”。我不確定這是不是一個好主意,因為在某些情況下這可能會非常具有破壞性。我個人認為我更喜歡讓人們等待,但至少得到他們應該回來的回應而不是隨機頁面。但是如果你需要它就在那裏。
請註意,web.config內容是可選的。如果您不提供IIS,則會訪問默認站點鏈接(/),即使在該請求結束時沒有匹配的請求,它仍將通過IIS管道觸發請求。理想情況下,您希望確保使用默認頁面命中ASP.NET端點,或者通過指定initializationPage以確保ASP.NET實際受到攻擊,因為IIS可能僅針對靜態頁面觸發非托管請求(取決於您的方式)管道已配置)。
AppDomain重啟怎麽樣?
除了IIS級別的完整工作進程回收之外,ASP.NET還必須處理AppDomain關閉,這可能由於各種原因而發生:
- 文件在BIN文件夾中更新
- Web部署到您的站點
- web.config已更改
- 硬應用程序崩潰
這些操作不會導致工作進程重新啟動,但它們確實會導致ASP.NET卸載當前的AppDomain並啟動新的AppDomain。由於上述功能僅適用於應用程序池重新啟動,因此AppDomain重新啟動也可能導致“ASP.NET服務”在後臺停止處理。
為了使應用程序在AppDomain上循環運行,您可以在Application_End事件中使用簡單的ping:
protected void Application_End() { var client = new WebClient (); var url = App .AdminConfiguration.MonitorHostUrl + “ping.aspx” ; client.DownloadString(URL); Trace .WriteLine(“應用程序關閉Ping:” + url); }
它會在管道關閉的最後將任何ASP.NET URL激活到當前站點,從而確保站點立即重新啟動。
ApplicationHost.config中的手動配置
上面的UI對應於以下ApplicationHost.config設置。如果您使用的是IIS 7,則這些標誌沒有UI,因此您必須手動編輯它們。
將Application Initialization組件安裝到IIS時,它應該將模塊自動配置為ApplicationHost.config。對我來說不幸的是,墨菲先生對我來說是最好的形式,模塊註冊沒有發生,我不得不手動添加它。
< globalModules > < add name = “ ApplicationInitializationModule ” image = “ %windir%\ System32 \ inetsrv \ warmup.dll ” /> </ globalModules >
很可能你不需要添加它,但如果事情不起作用,那麽檢查模塊是否實際註冊是值得的。
接下來,您需要配置ApplicationPool和Web站點。以下是ApplicationHost.config中的兩個相關條目。
< system.applicationHost > < applicationPools > < 添加名稱= “ 西風西風Web連接 ” AUTOSTART = “ 真” STARTMODE = “ AlwaysRunning ” managedRuntimeVersion = “ V4.0 ” managedPipelineMode = “ 集成” > < processModel identityType = “ LocalSystem ” setProfileEnvironment = “ true ” /> </ add > </ applicationPools > < sites > < site name = “ 默認網站” id = “ 1 ” > < application path = “/ MPa.Workflow.WebQueueMessageManager ” applicationPool = “ West Wind West Wind Web Connection ” preloadEnabled = “ true ” > < virtualDirectory path = “ / ” physicalPath = “ C:\ Clients \ ... ” /> </ application > </ site > </ sites > </ system.applicationHost >在應用程序池上,確保將autoStart和startMode標誌分別設置為true和AlwaysRunning。在站點上,確保將preloadEnabled標誌設置為true。
這就是你應該需要的。您仍然可以設置上述web.config設置。
ASP.NET即服務?
在我目前正在處理的特定應用程序中,我們有一個隊列管理器,它作為獨立服務運行,輪詢數據庫隊列並選擇作業並在多個線程上處理它們。該服務可以啟動任意數量的線程,並在IIS運行時自己將這些線程保持活動狀態。這些線程是新創建的線程,因此它們完全位於IIS線程池之外。為了使這項服務能夠工作,它所需要的只是一個長時間運行的引用,它可以在應用程序的整個生命周期內保持活動狀態。
在這個特定的應用程序中,有兩個組件在後臺運行在自己的線程上:一個調度程序,它運行各種計劃任務並處理諸如拾取電子郵件以發送到IIS範圍之外的事件和QueueManager。
以下是global.asax中的內容:
公共類Global :System.Web。HttpApplication { private static ApplicationScheduler scheduler; 私有靜態ServiceLauncher 發射器; protected void Application_Start(object sender,EventArgs e) { // ping服務並確保它保持活動 scheduler = new ApplicationScheduler () { CheckFrequency = 600000 }; scheduler.Start(); launcher = new ServiceLauncher (); launcher.Start(); //註冊所以關閉是受控的 HostingEnvironment .RegisterObject(啟動器); }
}
通過將這些對象保持為在啟動時僅設置一次的靜態實例,它們可以在應用程序的生命周期中存活。除了我可以刪除Windows服務接口(OnStart,OnStop,OnResume等)所需的各種覆蓋之外,這些類中的代碼與Windows服務代碼基本相同。否則行為和操作非常相似。
在這個應用程序中,ASP.NET有兩個目的:它充當SignalR的主機,並提供允許遠程管理“服務”的管理界面。我可以通過非常輕松地關閉ApplicationScheduler來遠程啟動和停止服務。我也可以通過SignalR服務直接通過幾個Web請求或(如我們現在所做)直接從隊列中提取統計信息。
使用ASP.NET註冊後臺對象
另請註意HostingEnvironment.RegisterObject()的使用。此函數向ASP.NET註冊一個對象,讓它知道它是一個後臺任務,如果AppDomain關閉,應該通知它。RegisterObject()需要一個帶有Stop()方法的接口,該方法被觸發並允許代碼響應關閉請求。以下是啟動器上IRegisteredObject :: Stop()方法的樣子:
public void Stop(bool immediate = false ) { LogManager .Current.LogInfo(“QueueManager Controller Stopped。” ); Controller.StopProcessing(); Controller.Dispose(); Thread .Sleep(1500); //給一些背景線程 HostingEnvironment .UnregisterObject(this ); }
實現IRegisterObject應該有助於AppDomain關閉的可靠性。感謝Justin Van Patten在推特上向我指出這一點。
RegisterObject()不是必需的,但我強烈建議在AppDomain關閉時,在後臺處理所有幹凈關閉的任何對象控件上實現它。
測試出來
我仍然處於這個特定服務的測試階段,看看是否有任何副作用。但到目前為止看起來並不像。通過大約50行代碼,我能夠將Windows服務啟動替換為Web啟動 - 其他一切都按原樣工作。值得一提的是SignalR 2.0的oWin托管,因為新的基於oWin的托管不需要代碼更改,只需要幾個配置文件設置和匯編指令,指向SignalR啟動類。甜!
與自托管相比,似乎SignalR在IIS內運行速度明顯更快。由於預加載,啟動感覺更快。
啟動和停止“服務”
由於應用程序作為Web服務器運行,因此可以輕松地使用Web界面來啟動和停止在服務內部運行的服務。對於我們的隊列管理器,SignalR服務和前端監控應用程序有一個用於切換隊列的播放和停止按鈕。
如果您想要更多的管理控制並使其更像Windows服務,您還可以從命令行顯式停止應用程序池,這相當於停止和重新啟動服務。
要從命令行啟動和停止,您可以使用IIS appCmd工具。停止:
>%windir%\ system32 \ inetsrv \ appcmd stop apppool /apppool.name:"Weblog“
並開始
>%windir%\ system32 \ inetsrv \ appcmd start apppool /apppool.name:"Weblog“
請註意,當您明確強制AppPool停止在UI(在ApplicationPools頁面上使用Start / Stop)或通過命令行工具運行時,應用程序池將不會立即自動重新啟動。您必須手動啟動它。
有什麽不喜歡的?
在IIS中運行後臺服務肯定有很多好處,但是...... ASP.NET應用程序在內存占用方面確實有更多的開銷,啟動時間稍微慢一些,但通常對於服務器應用程序來說這不是什麽大問題。如果應用程序穩定,則服務應該啟動並無限期地保持運行。很多時候,這種服務接口可以簡單地附加到現有的Web應用程序,或者如果需要將可伸縮性卸載到它自己的Web服務器上。
更容易使用
但這裏的最終好處是使用Web應用程序而不是服務更容易。在開發過程中,我可以簡單地通過點擊網站上的頁面來關閉自動啟動功能並通過IIS按需啟動服務。如果我想關閉IISRESET -stop將足夠輕松地關閉服務。然後我可以在任何我想要的地方附加一個調試器,這就像任何其他ASP.NET應用程序一樣。是的,你最終會在後臺線程上進行調試,但是Visual Studio處理得很好,如果你留在一個線程上,這與調試任何其他代碼沒什麽不同。
摘要
使用ASP.NET運行後臺服務操作可能不是一個超常見的場景,但它可能應該是構建服務時仔細考慮的事情。許多應用程序具有類似於服務的功能以及Application Initialization模塊的自動啟動功能,因此很容易將此功能構建到ASP.NET中。特別是當與SignalR的通知功能結合使用時,創建豐富的服務變得非常非常容易,這些服務也可以輕松地將其狀態傳達給外界。
無論是現有的應用程序需要一些後臺處理來安排相關任務,還是只是創建一個單獨的站點來托管您的服務,這很容易做到,您可以利用您已經用於其他Web項目的相同工具鏈。如果你有很多服務項目,值得考慮......給它一些思考......
您可能也喜歡的其他帖子
- 在Windows上使用Let加密IIS
- 使用Powershell自動執行IIS功能安裝
- 返回基礎:.NET Web Apps中的UTC和時區
- ASP.NET核心中對Kestrel和IIS Express的外部網絡訪問
使用IIS應用程序初始化來保持ASP.NET應用程序的活動