最近幾個月我一直在使用Windows服務工做,並且事實證實,Windows服務是調試,部署,更新和維護的重要因素。獲取服務設置,調試和更新的過程是一項重要的工做,必須進行普遍的文檔記錄和/或自動化。在構建服務的大多數項目中,人們最終爭先恐後地爭取用於管理的正確「流程」。另外一方面,Web應用程序的部署和維護是常見的,而且如今已經很好理解,由於咱們一直在處理Web應用程序。Web Tools中內置了大量基礎架構和工具,如Visual Studio,以促進流程。相比之下,Windows服務或任何自託管的東西彷佛使人費解。web
事實上,在最近的一篇博文中,我提到在最近的一個項目中,我一直在Windows服務中使用自託管SignalR,由於該應用程序其實是一個「服務」,也須要發送大量的消息經過SignalR。但實際狀況是,它也多是一個IIS應用程序,其中包含在後臺運行的服務組件。不管你是哪一種方式,它都是帶有內置Web服務器的Windows服務,或運行服務應用程序的IIS應用程序,它們都不遵循標準的服務或Web應用程序模板。shell
我我的更喜歡Web應用程序。在IIS內部運行我得到了IIS平臺的全部好處,包括服務生命週期管理(崩潰和重啓),受控關閉,整個安全基礎設施,包括簡單的證書支持,代碼的熱插拔以及從中直接發佈到IIS的能力在Visual Studio中輕鬆完成。數據庫
因爲這些好處,咱們開始從自託管服務轉移到ASP.NET Web應用程序。安全
ASP.NET即服務的缺失連接:自動加載
過去我曾想在ASP.NET中運行「相似服務」的應用程序,由於當你考慮它時,遠程控制Web應用程序要容易得多。服務被鎖定在啓動/中止操做中,但若是您在Web應用程序內部託管,則能夠編寫本身的票證並從任何位置控制它。事實上,差很少10年前,我構建了一個在ASP.NET內部運行的後臺調度應用程序,它運行良好,而且它仍在運行,正在完成它的工做。服務器
如今,在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中的可選安裝組件:
app
這是一個可選組件,所以請確保明確選擇它。ide
應用程序初始化的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項目的相同工具鏈。若是你有不少服務項目,值得考慮......給它一些思考......