1. 程式人生 > >使用IIS應用程序初始化來保持ASP.NET應用程序的活動

使用IIS應用程序初始化來保持ASP.NET應用程序的活動

特定 comm 熱插拔 自動化 sch stop blank 受到攻擊 站點

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服務是調試,部署,更新和維護的重要因素。獲取服務設置,調試和更新的過程是一項重要的工作,必須進行廣泛的文檔記錄和/或自動化。在構建服務的大多數項目中,人們最終爭先恐後地爭取用於管理的正確“流程”。

另一方面,Web應用程序的部署和維護是常見的,並且現在已經很好理解,因為我們一直在處理Web應用程序。Web Tools中內置了大量基礎架構和工具,如Visual Studio,以促進流程。相比之下,Windows服務或任何自托管的東西似乎令人費解。

事實上,在最近的一篇博文中,我提到在最近的一個項目中,我一直在Windows服務中使用自托管SignalR,因為該應用程序實際上是一個“服務”,也需要發送大量的消息通過SignalR。但實際情況是,它也可能是一個IIS應用程序,其中包含在後臺運行的服務組件。無論你是哪種方式,它都是帶有內置Web服務器的Windows服務,或運行服務應用程序的IIS應用程序,它們都不遵循標準的服務或Web應用程序模板。

我個人更喜歡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中的可選安裝組件:

技術分享圖片

這是一個可選組件,因此請確保明確選擇它。

應用程序初始化的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 ConnectionpreloadEnabled = 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應用程序的活動