1.ASP.NET的底层体系1html
2.ASP.NET的底层体系2 程序员
学习ASP.NET,要想作的更好,不了解ASP.NET是不行的。一个有理想的程序员都会像挤压海绵同样,努力的去学习和获取宝贵的知识。web
本篇文档内容,转自小皓园的博文--->(翻译)从底层了解ASP.NET体系结构,以此来尊重原创精神,并对其进行提炼,去掉一些无关的内容。api
ASP.NET是一个请求处理引擎,它获取客户端的请求,而后经过内置的管道,把请求传递到终点,在这个终点,开发者能够添加处理这个请求的逻辑代码。浏览器
整个ASP.NET引擎构建在托管代码里,全部的扩展功能都是经过托管代码的扩展提供。缓存
使用ASP.NET能够完成一些任务,以前这些任务是使用IIS的ISAPI扩展和过滤来完成的,尽管还有一些限制,但与ASP相比,已经有了很大的进步。ISAPI是底层Win32样式的API,它的接口就有1M,这样对于大型程序开发是很是困难的,因为ISAPI是底层的接口,所以它的速度也是很快的。安全
微软的ASP.NET和IIS的接口是经过宿主在.NET里的ISAPI扩展来通讯的,ISAPI提供了与WebServer通讯的核心接口,而后ASP.NET使用非托管代码获取请求发出响应。服务器
HTTP运行时提供了一套复杂的机制,在处理请求的每个层面都牵涉许多对象,但大多数对象均可以经过派生或事件接口来扩展。经过HTTP,能够进入较低层次的接口如:缓存、身份验证、受权等信息。能够在处理请求以前或以后过滤内容等等。多线程
咱们从一个典型的ASP.NET Web请求的生命周期开始提及。用户经过在浏览器输入一个URL,点击连接,提交一个HTML表单(一个POST请求),或者一个客户端程序调用基于ASP.NET的Web Services。在服务端,IIS收到这个请求。异步
ASP.NET的底层经过ISAPI扩展与IIS通讯,而后,经过ASP.NET这个请求一般被路由到一个带.aspx扩展名的页面,可是这个处理过程如何工做,则彻底依赖于HTTP处理器(handler)的执行。在IIS中,.aspx经由“应用程序扩展”被映射到ASP.NET ISAPI的dll文件:aspnet_isapi.dll,每个触发ASP.NET的请求,都必须由一个已经注册的,而且指向aspnet_isapi.dll的扩展名来标识。
依靠扩展名,ASP.NET把一个请求路由到一个恰当的处理器,该处理器则负责处理这个请求。
例子:Web Service的扩展名.asmx不会把一个请求路由到磁盘的某一个页面,而是会路由到定义中附加了指定特性的类,此特性会把它标记为一个Web Services的实现。
ISAPI是底层非托管的Win32 API,它定义的接口很是的单一且性能最优。用这些接口处理原始指针(raw pointer),而函数指针列表(function pointer)则用于回调。ISAPI提供了最底层的、高性能的接口。
ISAPI趋向于被当作桥接口使用,用于给高层级的工具提供应用服务类型的功能。
例子:ASP.NET就是构建在ISAPI之上,ISAPI给高层次的应用程序提供了高性能垂直访问接口,使得高层次的应用程序须要的信息能够从ISAPI提供的信息中提炼,引擎能够提炼ISAPI接口提供的表单里的对象有:Request Response。
做为约定,ISAPI支持ISAPI扩展(extensions)和ISAPI过滤(filters),扩展是请求处理接口,提供了跟Web Server输入和输出相关的逻辑处理。从本质来讲,它是一个事务接口。
ISAPI是钩子接口,它容许你查看进入IIS的每个请求并能够修改请求的内容。
IIS把不一样的扩展名如.aspx映射到ASP.NET的ISAPI扩展,经过这种机制,在WEB SERVER里,请求就能够被路由到ASP.NET的处理管道里。
当一个请求进来的时候,IIS会检查脚本映射,而后把请求路由到aspnet_isapi.dll。IIS 6总会保持一个单独的工做进程:应用程序池,全部的处理都发生在这个进程里。对于IIS 6来讲,应用程序池是一个重大的改进,由于他们容许以更小的粒度控制一个指定进程的执行。你能够为每个虚拟目录或者整个web站点配置应用程序池,这样它能够与运行在同一台机器的其余程序彻底隔离。
应用程序池是高度可配置的,经过设置应用程序池的执行许可,能够配置他们的执行环境。对于ASP.NET而言,IIS 6最大的改进是使用应用程序池代替了machine.config里的ProcessModel实体的大部分功能。在IIS 5里,这个实体是很难管理的,由于它的设置是全局的,并且不可以在指定WEB程序的wen.config里覆盖这些设置。当IIS 6运行的时候,ProcessModel里的大部分配置将被忽略,取而代之的是读取应用程序池的配置。另一些配置,像线程池的大小和IO线程数目等仍然仍是要经过这个节点的配置。
IIS 6应用程序池也包含了ASP.NET固有的东西,ASP.NET能够和新的底层API通讯,这些API容许直接访问HTTP缓冲存储器的API,而HTTP缓冲存储器的API能够直接进入Web SERVER的缓冲存储器,卸载ASP.NET级别的缓存。
在IIS 6里,ISAPI扩展运行在应用程序池的工做进程里,而.NET运行时也在这个进程里,因此ISAPI扩展和.NET运行时通讯是发生在进程内。
进入.NET运行时真正登陆点发生在一些没有正式文档的类和接口之间。工做进程w3wp.exe宿主在.NET运行里。ISAPI dll经过底层的COM调用一小撮非托管类型的接口,其实最终调用的是ISAPIRuntime派生类的实例。进入运行时的第一个登陆点是未归档的ISAPIRuntime类,它经过COM把接口IISAPIRuntime暴露给调用者,这些COM接口是底层的IUnknown。基于这些接口,就意味着ISAPI到ASP.NET之间的调用属于内部调用。
IISAPIRuntime接口担当着来自ISAPI扩展的非托管代码和托管代码之间的桥梁
[return:MarshallAs(UnmanagerType.I4)]
int ProcessRequest(IntPtr ecb,[In,MarshallAs(UnmanagerType.I4)] int useProcessMode)
ecb参数是ISAPI扩展控制块,它做为非托管资源传递给ProcessRequest方法,此方法将获取ECB,而后做为基本的输入和输出接口,用于Request和Response对象。ISAPI ECB包含着全部底层的请求信息,这其中包括服务器变量、用于表单变量的输入流和写数据并把数据发送到客户端的输出流中。一个单独的ECB引用基本提供了一个ISAPI请求能够访问的全部功能。
ISAPI扩展以异步的方式处理请求,因此当ISAPI扩展调用了工做进程或者IIS的线程后,会当即返回,但会为当前的请求保留ECB,所以,ECB须要包含这样的机制,当请求结束的时候通知ISAPI,而后ISAPI扩展释放ECB资源。接着以异步的方式当即释放ISAPI的工做线程和卸载由ASP.NET托管的那个隔离的处理线程。
ASP.NET获得ECB引用后,会在内部使用它来获取当前请求的相关信息。如服务器变量。ECB将就存活直到这个请求结束或者IIS超时,在这以前,ASP.NET将会与ecb继续保持通讯,当请求结束的时候,输出的内容会写进ISAPI的输出流中。而后ISAPI扩展会被通知请求已经结束,让它知道ECB能够被释放了,这个执行过程是很是高效的。
ISAPI是多线程的,所以请求能够以多线程的方式穿过AppDomainFactory.Create()返回的对象引用。
public int ProcessRequest(IntPtr ecb,int iWRType) { //ISAPIWorkerRequest从HttpWorkRequest继承,这里建立的是一个ISAPIWorkerRequest派生类的一个实例 HttpWorkerRequest request=ISAPIWorkerRequest.CreateWorkerRequest(ecb,iWRType); //获得请求的物理路径 string str=request.GetAppPathTranslated(); //获得AppDomain的物理路径 string str2=HttpRuntime.AppDomainAppPathInternal; if( str2==null || str.Equals(".") || string.Compare(str,str2,true,CultureInfo.InvariantCulture)==0) { HttpRuntime.ProcessRequest(request); return 0; } //若是外部请求的AppDomain物理路径和原来的AppDomain的路径不一样,说明ISAPI维持的AppDomain已经失效,因此须要把原来的城西关闭 HttpRuntime.ShutDownAppDomain("from "+str+str2); return 1; }
上述代码ProcessRequest接收一个ISAPI ecb对象和一个服务器类型参数,这个线程是安全的,所以多个ISAPI线程能够同时安全的调用单个返回对象的实例。
System.Web.Hosting.ISAPIWorkerRequest继承抽象类HttpWorkerRequest,它的职责是建立一个抽象的输入和输出视图,为Web程序的输入提供服务。上面的代码中有一个方法CreateWorkerRequest,这个方法的第二个参数用于指定建立什么样的工做请求对象,即ISAPIWorkerRequest的派生类,这里有三个不一样的版本:ISAPIWorkerRequestInProc,ISAPIWorkerRequestInProcForIIS6,ISAPIWorkerRequestOutOfProc。当这个请求到来时,这个ISAPIWorkerRequest将被建立,用于给Request和Response对象提供基础服务,而这两个对象将从数据的提供者WorkerRequest接收数据流。
抽象类HttpWworkerRequest围绕着底层的接口提供了高层的抽象,这样就不用考虑数据的来源,不管他是一个CGI Web Server,Web浏览器仍是自定义的机制(用于把数据流入HTTP运行),ASP.NET均可以以一样的方式从中获取数据。
有关IIS的抽象主要集中在ISAPI ECB块,在咱们的请求处理当中,ISAPIWorkerRequest依赖于ISAPI ECB块。
ISAPIWorkerRequest实现了一个高层次包装器方法,它调用了低层次的核心方法,而这些方法负责实际调用非托管API或者是“服务层的实现”,核心的方法在ISAPIWorkerRequest的派生类中得以实现,这样能够针对它宿主的环境提供特定的实现,为之后增长一个额外环境的实现类做为新的Web Server接口提供了便利。
说句实话,上面的内容讲了这么多,头都被绕晕了。讲来说去总结就是如下几点:
1.用户在浏览器输入一个URL,提交一个HTML表单。服务端的IIS接收到请求,ASP.NET的底层ISAPI与IIS通讯,路由到指定的页面。
2.ISAPI是底层非托管的win32 API,ASP.NET都是构建在ISAPI之上,它给高层次的应用程序提供了高性能垂直访问接口。
3.在ISAPI扩展里,当第一个请求命中一个ASP.NET的映射扩展时,工做线程就会引导.NET运行时启动。一旦运行时存在了,非托管代码就能够为指定的虚拟目录请求一个ISAPRuntime的实例,固然这个前提是这个实例还不存在,每一个虚拟目录都会拥有一个AppDomain,ISAPIRuntime存在于AppDomain中,它引导一个单独的程序启动。