文章引导

1.ASP.NET的底层体系1

2.ASP.NET的底层体系2

引言:

学习ASP.NET,要想做的更好,不了解ASP.NET是不行的。一个有理想的程序员都会像挤压海绵一样,努力的去学习和获取宝贵的知识。

本篇文档内容,转自小皓园的博文--->(翻译)从底层了解ASP.NET体系结构,以此来尊重原创精神,并对其进行提炼,去掉一些无关的内容。

一.ASP.NET是什么

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运行时提供了一套复杂的机制,在处理请求的每一个层面都牵涉许多对象,但大多数对象都可以通过派生或事件接口来扩展。通过HTTP,可以进入较低层次的接口如:缓存、身份验证、授权等信息。可以在处理请求之前或之后过滤内容等等。

三.从浏览器到ASP.NET

我们从一个典型的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

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的每一个请求并可以修改请求的内容。

ASP.NET的底层体系1-LMLPHP

IIS把不同的扩展名如.aspx映射到ASP.NET的ISAPI扩展,通过这种机制,在WEB SERVER里,请求就可以被路由到ASP.NET的处理管道里。

五.IIS5   IIS6

当一个请求进来的时候,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运行时通信是发生在进程内。

ASP.NET的底层体系1-LMLPHP

六.进入.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可以被释放了,这个执行过程是非常高效的。

七.ProcessRequest

ISAPI是多线程的,因此请求可以以多线程的方式穿过AppDomainFactory.Create()返回的对象引用。

ASP.NET的底层体系1-LMLPHP

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)==)
{
HttpRuntime.ProcessRequest(request);
return ;
} //如果外部请求的AppDomain物理路径和原来的AppDomain的路径不同,说明ISAPI维持的AppDomain已经失效,所以需要把原来的城西关闭
HttpRuntime.ShutDownAppDomain("from "+str+str2);
return ;
}

上述代码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中,它引导一个单独的程序启动。

05-26 17:15