在年月份我曾经写了三篇详细介绍IIS架构和ASPNET运行时管道的文章深入介绍了IIS x与IIS HTTP请求的监听与分发机制以及ASPNET运行时管道对HTTP请求的处理流程很多人留言为何没有IIS 的介绍在写作《WCF深入剖析》中为了剖析基于IIS的WCF服务寄宿(Hosting)再次对相关内容进行了研究在这里一并与大家分享
IIS x与ASPNET
我们先来看看IIS x是如何处理基于ASPNET资源(比如aspxasmx等)请求的整个过程基本上可以通过图体现
IIS x运行在进程InetInfoexe中在该进程中一个最重要的服务就是名为World Wide Web Publishing Service(简称WSVC)的Windows ServiceWSVC的主要功能包括HTTP请求的监听工作进程的管理以及配置管理(通过从Metabase中加载相关配置信息)等
当检测到某个HTTP Request后先根据扩展名判断请求的是否是静态资源(比limgtxtxml等)如果是则直接将文件内容以HTTP Response的形式返回如果是动态资源(比如aspxaspphp等等)则通过扩展名从IIS的脚本影射(Script Map)找到相应的ISAPI Dll
ISAPI是Internet服务器API(Internet Server Application Programming Interface)的缩写是一套本地的(Native)Win API具有较高的执行性能是IIS和其他动态Web应用或者平台之间的纽带比如ASP ISAPI桥接IIS与ASP而ASPNET ISAPI则连接着IIS与ASPNETISPAI定义在一个Dll中ASPNET ISAPI对应的Dll为Aspnet_isapidll你可以在目录%windir%\MicrosoftNET\Framework\{version no}\中找到该Dll
ISAPI支持ISAPI扩展(ISAPI Extension)和ISAPI筛选(ISAPI Filter)前者是真正处理HTTP请求的接口后者则可以在HTTP请求真正被处理之前查看修改转发或者拒绝请求比如IIS可以利用ISAPI筛选进行请求的验证(Authentication)
如果我们请求的是一个基于ASPNET的资源类型比如aspx Web Page asmx Web Service或者svc WCF Service等Aspnet_isapidll会被加载ASPNET ISAPI扩展会创建ASPNET的工作进程(如果该进程尚未启动)对于IIS x来说该工作进程为aspnetexeIIS进程与工作进程之间通过命名管道(Named Pipes)进程通信以获得最好的性能
在工作进程初始化过程中NET 运行时(CLR)被加载从而构建了一个托管的环境对于某个Web应用的初次请求CLR会为其创建一个AppDomain在此AppDomain中HTTP运行时(HTTP Runtime)被加载并用以创建相应的应用对于寄宿于IIS x的所有Web 应用都运行在同一个进程(工作进程Aspnet_wpexe)的不同AppDomain中
IIS 与ASPNET
通过上面的介绍我们可以看出IIS x至少存在着如下两个方面的不足
ISAPI Dll被加载到InetInfoexe进程中它和工作进程之间是一种典型的跨进程通信方式尽管采用性能最好的命名管道但是仍然会带来性能的瓶颈 所有的ASPNET应用运行在相同的进程(aspnet_wpexe)中的不同的应用程序域(AppDomain)中基于应用程序域的隔离级别不能从根本上解决一个应用程序对另一个程序的影响在更多的时候我们需要不同的Web应用运行在不同的进程中 在IIS 中为了解决第一个问题ISAPIdll被直接加载到工作进程中为了解决第个问题引入了应用程序池(Application Pool)的机制我们可以为一个或者多个Web应用创建应用程序池每一个应用程序池对应一个独立的工作进程从而为运行在不同应用程序池中的Web应用提供基于进程的隔离级别IIS 的工作进程名称为wwpexe
当然除了上面两点改进之外IIS 还有其他一些值得称道的地方其中最重要的一点就是创建了一个新的HTTP监听器HTTP协议栈(HTTP Protocol StackHTTPSYS)HTTPSYS运行在Windows的内核模式(Kernel Mode)下作为驱动程序而存在它是Windows 的TCP/IP网络子系统的一部分从结构上它属于TCP之上的一个网络驱动程序严格地说HTTPSYS已经不属于IIS的范畴了所以HTTPSYS的配置信息并不保存在IIS的元数据库(Metabase)而是定义在注册表中HTTPSYS的注册表项位于下面的路径中HKEY_LOCAL_MACHINE/SYSTEM/CurrentControlSet/Services/HTTPHTTPSYS能够带来如下的好处
持续监听由于HTTPSYS是一个网络驱动程序始终处于运行状态对于用户的HTTP请求能够及时作出反应 更好的稳定性HTTPSYS运行在操作系统内核模式下并不执行任何用户代码所以其本身不会受到Web应用工作进程和IIS进程的影响 内核模式下数据缓存如果某个资源被频繁请求HTTPSYS会把响应的内容进行缓存缓存的内容可以直接响应后续的请求由于这是基于内核模式的缓存不存在内核模式和用户模式的切换响应速度将得到极大的改进 图体现了IIS的结构和处理HTTP请求的流程从中可以看出与IIS x不同WSVC从InetInfoexe进程脱离出来(对于IIS来说InetInfoexe基本上可以看作单纯的IIS管理进程)运行在另一个进程SvcHostexe中不过WSVC的基本功能并没有发生变化只是在功能的实现上作了相应的改进与IIS x一样元数据库(Metabase)依然存在于InetInfoexe进程中
当HTTPSYS监听到用户的HTTP请求后将其分发给WSVCWSVC解析出请求的URL并根据从Metabase获取的URL与Web应用之间的映射关系得到目标应用并进一步得到目标应用运行的应用程序池或者工作进程如果工作进程不存在(尚未创建或者被回收)则为该请求创建新的工作进程工作进程的这种创建方式被称为请求式创建在工作进程的初始化过程中相应的ISAPIdll被加载对于ASPNET应用来说被加载的ISAPIdll为Aspnet_ispaidllASPNET ISAPI再负责进行CLR的加载AppDomain创建Web Application的初始化等
IIS 与ASPNET
IIS 对请求的监听和分发机制上又进行了革新性的改进主要体现在对于Windows进程激活服务(Windows Process Activation ServiceWAS)的引入将原来(IIS )WSVC承载的部分功能分流给了WAS具体来说通过上面的介绍我们知道对于IIS 来说WSVC主要承载着三大功能
HTTP请求接收接收HTTPSYS监听到的HTTP请求 配置管理从元数据库(Metabase)中加载配置信息对相关组件进行配置 进程管理创建回收监控工作进程 在IIS 后两组功能被移入WAS中接收HTTP请求的任务依然落在WSVC头上WAS的引入为IIS 一项前所未有的特性同时处理HTTP和非HTTP请求在WAS中通过一个重要的接口监听器适配器接口(Listener Adapter Interface)抽象出不同协议监听器监听到的请求至于IIS下的监听器除了基于网络驱动的HTTPSYS提供HTTP请求监听功能外WCF提供了种类型的监听器TCP监听器命名管道(Named Pipes)监听器和MSMQ监听器分别提供了基于TCP命名管道和MSMQ传输协议的监听功能与此种监听器相对的是种监听器适配器(Adapter)提供监听器与监听器适配器接口之间的适配从这个意义上讲IIS 中的WSVC更多地为HTTPSYS起着监听适配器的功能WCF提供的这种监听器和监听适配器定义在程序集SMHostexe中你可以通过下面的目录找到该程序集%windir%\MicrosoftNET\Framework\v\Windows Communication Foundatio
WCF提供的这种监听器和监听适配器最终以Windows Service的形式体现虽然它们定义在一个程序集中我们依然通过服务工作管理器(SCMService Control Manager)对其进行单独的启动终止和配置SMHostexe提供了个重要的Windows Service
NetTcpPortSharing为WCF提供TCP端口共享关于端口共享 NetTcpActivator为WAS提供基于TCP的激活请求包含TCP监听器和对应的监听适配器 NetPipeActivator为WAS提供基于命名管道的激活请求包含命名管道监听器和对应的监听适配器 NetMsmqActivator为WAS提供基于MSMQ的激活请求包含MSMQ监听器和对应的监听适配器 图为上述的个Windows Service在服务控制管理器(SCM)中的呈现
图 定义在SMHostexe中的Windows Service
图揭示了IIS 的整体构架以及整个请求处理流程无论是从WSVC接收到的HTTP请求还是通过WCF提供的监听适配器接收到的请求最终都会传递到WAS如果相应的工作进程(或者应用程序池)尚未创建其创建之否则将请求分发给对应的工作进程进行后续的处理WAS在进行请求处理过程中通过内置的配置管理模块加载相关的配置信息对相关的组建进行配置与IIS x和IIS 基于Metabase的配置信息存储不同的是IIS 大都将配置信息存放于XML形式的配置文件中基本的配置存放在fig中
图 IIS 与ASPNET
ASPNET集成
从上面对IIS x和IIS 的介绍中我们不难发现这一点IIS与ASPNET是两个相互独立的管道(Pipeline)在各自管辖范围内它们各自具有自己的一套机制对HTTP请求进行处理两个管道通过ISAPI实现联通IIS是第一道屏障当对HTTP请求进行必要的前期处理(比如身份验证等)后通过ISAPI将请求分发给ASPNET管道当ASPNET在自身管道范围内完成对HTTP请求的处理后处理后的结果再返回到IISIIS对其进行后期处理(比如日志记录压缩等)最终生成HTTP响应(HTTP Response)从另一个角度讲IIS运行在非托管的环境中而ASPNET管道则是托管的从这个意义上讲ISAPI还是连接非托管环境和托管环境的纽带图反映了IIS 与ASPNET之间的桥接关系
图 基于IIS 与ASPNET双管道设计
IIS x和IIS 下把两个管道进行隔离至少带来了下面一些局限与不足
相同操作的重复执行IIS与ASPNET之间具有一些重复的操作比如身份验证 动态文件与静态文件处理的不一致因为只有基于ASPNET的动态文件(比如aspxasmxsvc等等)的HTTP请求才能通过ASPNET ISAPI进入ASPNET管道而对于一些静态文件(比lxmlimg等)的请求则由IIS直接响应那么ASPNET管道中的一些功能将不能用于这些基于静态文件的请求比如我们希望通过Forms认证应用于基于图片文件的请求 IIS难以扩展对于IIS的扩展基本上就体现在自定义ISAPI但是对于大部分人来说这不是一件容易的事情因为ISAPI是基于Win的非托管的API并非一种面向应用的编程接口通常我们希望的是诸如定义ASPNET的HttpModule和HttpHandler一样通过托管代码的方式来扩展IIS 对于Windows平台下的IIS来讲ASPNET无疑是一等公民它们之间不应该是井水不犯河水的关系而应该是你中有我我中有你的关系为此在IIS 中实现了两者的集成对于集成模式下的IIS 我们获得如下的好处
允许我们通过本地代码(Native Code)和托管代码(Managed Code)两种方式定义IIS Module这些IIS Module注册到IIS中形成一个通用的请求处理管道由这些IIS Module组成的这个管道能够处理所有的请求不论请求基于怎样的资源类型比如可以将FormsAuthenticationModule提供的Forms认证应用到基于aspxCGI和静态文件的请求 将ASPNET提供的一些强大的功能应用到原来难以企及的地方比如将ASPNET的URL重写功能置于身份验证之前 采用相同的方式去实现配置检测和支持一些服务器特性(Feature)比如ModuleHandler映射错误定制配置(Custom Error Configuration)等
图 基于IIS 与ASPNET集成管道设计
图演示了在ASPNET集成模式下IIS整个请求处理管道的结构我们可以看到原来ASPNET提供的托管组件可以直接应用在IIS管道中
ASPNET管道
以IIS 为例在工作进程wwpexe中利用Aspnet_ispaidll加载NET运行时(如果NET运行时尚未加载)IIS 引入了应用程序池的概念一个工作进程对应着一个应用程序池一个应用程序池可以承载一个或者多个Web应用每个Web应用映射到一个IIS虚拟目录与IIS x一样每一个Web应用运行在各自的应用程序域中
如果HTTPSYS接收到的HTTP请求是对该Web应用的第一次访问当成功加载了运行时后会通过AppDomainFactory为该Web应用创建一个应用程序域(AppDomain)随后一个特殊的运行时IsapiRuntime被加载IsapiRuntime定义在程序集SystemWeb中对应的命名空间为SystemWebHostingIsapiRuntime会接管该HTTP请求
IsapiRuntime会首先创建一个IsapiWorkerRequest对象用于封装当前的HTTP请求并将该IsapiWorkerRequest对象传递给ASPNET运行时HttpRuntime从此时起HTTP请求正式进入了ASPNET管道根据IsapiWorkerRequest对象HttpRuntime会创建用于表示当前HTTP请求的上下文(Context)对象HttpContext
随着HttpContext被成功创建HttpRuntime会利用HttpApplicationFactory创建新的或者获取现有的HttpApplication对象实际上ASPNET维护着一个HttpApplication对象池HttpApplicationFactory从池中选取可用的HttpApplication用户处理HTTP请求处理完毕后将其释放到对象池中HttpApplicationFactory负责处理当前的HTTP请求
在HttpApplication初始化过程中会根据配置文件加载并初始化相应的HttpModule对象对于HttpApplication来说在它处理HTTP请求的不同的阶段会触发不同的事件(Event)而HttpModule的意义在于通过注册HttpApplication的相应的事件将所需的操作注入整个HTTP请求的处理流程ASPNET的很多功能比如身份验证授权缓存等都是通过相应的HttpModule实现的
而最终完成对HTTP请求的处理实现在另一个重要的对象中HttpHandler对于不同的资源类型具有不同的HttpHandler比如aspx页对应的HttpHandler为SystemWebUIPageWCF的svc文件对应的HttpHandler为SystemServiceModelActivationHttpHandler上面整个处理流程如图所示
图 ASPNET 处理管道
HttpApplication
HttpApplication是整个ASPNET基础架构的核心它负责处理分发给它的HTTP请求由于一个HttpApplication对象在某个时刻只能处理一个请求只有完成对某个请求的处理后HttpApplication才能用于后续的请求的处理所以ASPNET采用对象池的机制来创建或者获取HttpApplication对象具体来讲当第一个请求抵达的时候ASPNET会一次创建多个HttpApplication对象并将其置于池中选择其中一个对象来处理该请求当处理完毕HttpApplication不会被回收而是释放到池中对于后续的请求空闲的HttpApplication对象会从池中取出如果池中所有的HttpApplication对象都处于繁忙的状态ASPNET会创建新的HttpApplication对象
HttpApplication处理请求的整个生命周期是一个相对复杂的过程在该过程的不同阶段会触发相应的事件我们可以注册相应的事件将我们的处理逻辑注入到HttpApplication处理请求的某个阶段
我们接下来介绍的HttpModule就是通过HttpApplication事件注册的机制实现相应的功能的表按照实现的先后顺利列出了HttpApplication在处理每一个请求时触发的事件名称
表 名称
描述
BeginRequest
HTTP管道开始处理请求时会触发BeginRequest事件
AuthenticateRequestPostAuthenticateRequest
ASPNET先后触发这两个事件使安全模块对请求进行身份验证
AuthorizeRequestPostAuthorizeRequest
ASPNET先后触发这两个事件使安全模块对请求进程授权
ResolveRequestCachePostResolveRequestCache
ASPNET先后触发这两个事件以使缓存模块利用缓存的直接对请求直接进程响应(缓存模块可以将响应内容进程缓存对于后续的请求直接将缓存的内容返回从而提高响应能力)
PostMapRequestHandler
对于访问不同的资源类型ASPNET具有不同的HttpHandler对其进程处理对于每个请求ASPNET会通过扩展名选择匹配相应的HttpHandler类型成功匹配后该实现被触发
AcquireRequestStatePostAcquireRequestState
ASPNET先后触发这两个事件使状态管理模块获取基于当前请求相应的状态比如SessionState
PreRequestHandlerExecutePostRequestHandlerExecute
ASPNET最终通过一请求资源类型相对应的HttpHandler实现对请求的处理在实行HttpHandler前后这两个实现被先后触发
ReleaseRequestStatePostReleaseRequestState
ASPNET先后触发这两个事件使状态管理模块释放基于当前请求相应的状态
UpdateRequestCachePostUpdateRequestCache
ASPNET先后触发这两个事件以使缓存模块将HttpHandler处理请求得到的相应保存到输出缓存中
LogRequestPostLogRequest
ASPNET先后触发这两个事件为当前请求进程日志记录
EndRequest
整个请求处理完成后EndRequest事件被触发
对于一个ASPNET应用来说HttpApplication派生于globalasax文件我们可以通过创建globalasax文件对HttpApplication的请求处理行为进行定制globalasax采用一种很直接的方式实现了这样的功能这种方式既不是我们常用的方法重写(Method Overriding)或者事件注册而是直接采用方法名匹配在globalasax中我们按照这样的方法命名规则进行事件注册Application_{Event Name}比如Application_BeginRequest方法用于处理HttpApplication的BeginRequest事件如果通过VS创建一个globalasax文件下面是默认的定义
<%@ Application Language=C# %><script runat=server>void Application_Start(object sender EventArgs e) {}void Application_End(object sender EventArgs e) {}void Application_Error(object sender EventArgs e) {}void Session_Start(object sender EventArgs e) {}void Session_End(object sender EventArgs e) {}</script>HttpModule
ASPNET为创建各种NET Web应用提供了强大的平台它拥有一个具有高度可扩展性的引擎并且能够处理对于不同资源类型的请求那么是什么成就了ASPNET的高可扩展性呢? HttpModule功不可没
从功能上讲HttpModule之于ASPNET就好比ISAPI Filter之于IIS一样IIS将接收到的请求分发给相应的ISAPI Extension之前注册的ISAPI Filter会先截获该请求ISAPI Filter可以获取甚至修改请求的内容完成一些额外的功能与之相似地当请求转入ASPNET管道后最终负责处理该请求的是与请求资源类型相匹配的HttpHandler对象但是在Handler正式工作之前ASPNET会先加载并初始化所有配置的HttpModule对象HttpModule在初始化的过程中会将一些功能注册到HttpApplication相应的事件中那么在HttpApplication整个请求处理生命周期中的某个阶段相应的事件会被触发通过HttpModule注册的事件处理程序也得以执行
所有的HttpModule都实现了IHttpModule接口下面是IHttpModule的定义其中Init方法用于实现HttpModule自身的初始化该方法接受一个HttpApplication对象有了这个对象事件注册就很容易了
public interface IHttpModule{void Dispose();void Init(HttpApplication context);}ASPNET提供的很多基础构件(Infrastructure)功能都是通过相应的HttpModule实现的下面类列出了一些典型的HttpModule:
OutputCacheModule实现了输出缓存(Output Caching)的功能 SessionStateModule在无状态的HTTP协议上实现了基于会话(Session)的状态 WindowsAuthenticationModule + FormsAuthenticationModule + PassportAuthentication Module实现了种典型的身份认证方式Windows认证Forms认证和Passport认证 UrlAuthorizationModule + FileAuthorizationModule实现了基于Uri和文件ACL(Access Control List)的授权 而另外一个重要的HttpModule与WCF相关那么就是SystemServiceModel ActivationHttpModuleHttpModule定义在SystemServiceModel程序集中在默认的情况下HttpModule完成了基于IIS的寄宿工作
除了这些系统定义的HttpModule之外我们还可以自定义HttpMoudle通过nfig我们可以很容易地将其注册到我们的Web应用中
HttpHandler
如果说HttpModule相当于IIS的ISAPI Filter的话我们可以说HttpHandler则相当于IIS的ISAPI ExtensionHttpHandler在ASPNET中扮演请求的最终处理者的角色对于不同资源类型的请求ASPNET会加载不同的Handler来处理也就是说aspx page与asmx web service对应的Handler是不同的
所有的HttpHandler都实现了接口IHttpHandler下面是IHttpHandler的定义方法ProcessRequest提供了处理请求的实现
public interface IHttpHandler{void ProcessRequest(HttpContext context);bool IsReusable { get; }}对于某些HttpHandler具有一个与之相关的HttpHandlerFactory用于创建或者获取相应的HttpHandlerHttpHandlerFactory实现接口IHttpHandlerFactory方法GetHandler用于创建新的HttpHandler或者获取已经存在的HttpHandler
public interface IHttpHandlerFactory{IHttpHandler GetHandler(HttpContext context string requestType string url string pathTranslated);void ReleaseHandler(IHttpHandler handler);}HttpHandler和HttpHandlerFactory的类型都可以通过相同的方式配置到nfig中下面一段配置包含对种典型的资源类型的HttpHandler配置aspxasmx和svc可以看到基于WCF Service的HttpHandler类型为SystemServiceModelActivationHttpHandler
<?xml version= encoding=utf ?>
<configuration>
<systemweb>
<httpHandlers>
<add path=*svc verb=* type=SystemServiceModelActivationHttpHandler SystemServiceModel Version= Culture=neutral PublicKeyToken=bace validate=false/>
<add path=*aspx verb=* type=SystemWebUIPageHandlerFactory validate=True/>
<add path=*asmx verb=* type=SystemWebServicesProtocolsWebServiceHandlerFactory SystemWebServices Version= Culture=neutral PublicKeyToken=bfffdaa validate=False/>
</httpHandlers>
</systemweb>
</configuration>