最近几个月我一直在使用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项目的相同工具链。若是你有不少服务项目,值得考虑......给它一些思考......