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

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

另一方面,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中的可選安裝元件:

Windows功能

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

應用程式初始化的IIS配置

需要在應用程式池和IIS應用程式級別上應用初始化。從IIS 8開始,可以通過IIS管理控制檯進行這些設定。

從應用程式池開始:
AppPools

在這裡,你需要設定兩種自動開始其始終設定,並應設定為AlwaysRunning的STARTMODE。兩者都必須設定 - 預設情況下,Start Automatically標誌設定為true,並控制應用程式池本身的啟動,而啟動應用程式則需要Always Running標誌。如果沒有設定後一個標誌,則站點設定無效。

現在,在站點/應用程式級別,您可以指定站點是否應預載入:

SiteConfig

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專案的相同工具鏈。如果你有很多服務專案,值得考慮......給它一些思考......

您可能也喜歡的其他帖子