HTTP、Asp.net管道与IIS

HTTP、Asp.net管道与IIS

吴剑 2010-04-19

原创文章,转载必需注明出处:http://www.cnblogs.com/wu-jian

吴剑 http://www.cnblogs.com/wu-jian

前言

本文介绍了IIS、Http管道、Asp.Net管道一些相对较底层知识,个人能力有限,不足之处请及时指正。

吴剑 http://www.cnblogs.com/wu-jian

HTTP管道

ISAPI

当用户在浏览器中键入一个URL、点击一个超链接或提交一个HTML表单,在服务器端,IIS5或IIS6将会收到这个请求,并根据请求URL的扩展名不同交由不同的ISAPI扩展来处理,如下图所示:

ISAPI

ISAPI是底层非托管的win32 API,它定义的接口非常的单一并且性能最优。开发者和第三方厂商可以使用这些接口深入到IIS里。由于ISAPI是非常低层的,所以不太适合使用它构建应用级的程序。ISAPI趋向于被当作桥接口使用,用于给高层次的工具提供应用服务类型的功能,例如运行于IIS的Apache、Tomcat就是构建于ISAPI之上。ISAPI是个非常好的工具,它给高层次的应用程序提供了高性能垂直访问接口,这使得那些高层次的应用程序需要的信息可以从ISAPI提供的信息中提炼。在ASP和ASP.NET里,引擎可以提炼ISAPI接口提供的表单里的对象如:Request和Response,这些对象可以从ISAPI请求的信息中读取它们各自的内容。可以说,ISAPI是自定义Web请求处理中第一个并且具有最高性能的IIS入口点。

 

ISAPI扩展

作为约定,ISAPI支持ISAPI扩展(extensions)和ISAPI过滤(filters)。扩展是请求处理接口,提供了跟Web Server输入和输出相关的逻辑处理。从本质上来说,它是一个事务接口,ASP和ASP.NET都被看作ISAPI扩展的实现。ISAPI是钩子接口,它允许你查看进入IIS的每一个请求并且可以修改请求的内容(包括输入和输出)。
ASP.NET的ISAPI扩展为aspnet_isapi.dll,如下图所示:

ISAPI扩展

 

IIS 5与IIS 6

当一个aspx请求到达IIS时,会检查脚本映射,然后把请求路由到aspnet_isapi.dll。接下来这个DLL文件的操作是什么呢?在IIS5和IIS6里,这个请求又是如何到达ASP.NET运行时的呢?它们两者的处理方式有没有重大变化呢?

IIS5与IIS6

在IIS 5中:
1、客户端的请求进入IIS,IIS根据请求的URL试图找到一个扩展映射,当发现URL是一个.aspx时,则将请求交由aspnet_isapi.dll
2、ISAPI DLL再通过一些已命名的管道与ASP.NET工作者进程连接
3、ASP.NET把请求传递给一个指定的AppDomain
4、在AppDomain中开始走ASP.NET管道流程,这个流程经过了一系列的步骤,最终结果是将请求传递给服务终点(Endpoint)----我们编写的.aspx.cs类,这个类从Page派生,并实现IHttpHandler接口

IIS 6 工作原理

在IIS 6中:
IIS不再直接寄宿像ISAPI扩展的任何外部可执行代码。代替的是,IIS总会保持一个单独的工作进程:应用程序池。所有的处理都发生在这个进程里,包括ISAPI dll的执行。对于IIS6而言,应用程序池是一个重大的改进,因为它们允许以更小的粒度控制一个指定进程的执行。你可以为每一个虚拟目录或者整个Web站点配置应用程序池,这可以使你很容易的把每一个应用程序隔离到各自的进程里,这样就可以把它与运行在同一台机器上其他程序完全隔离。从Web处理的角度看,如果一个进程死掉,至少它不会影响到其它的进程。

吴剑 http://www.cnblogs.com/wu-jian

Asp.net管道

主要流程

ASP.NET流程图

从请求进入ASP.NET工作者进程,直至它到达最终的处理程序之前要经过一系列的步骤和过程,这个步骤和过程本文中将其称之为ASP.NET管道,关于ASP.NET管道我并未找到相关官方的标准文档,而是根据Fritz Onion的《ASP.NET基础教程》整理而来。

1、ASP.NET管道的第一步是创建HttpWorkerRequest对象,它包含与当前请求有关的所有信息,包括请求的URL、标题等等。

2、把请求传递给HttpRuntime类的静态ProcessRequest方法。
3、HttpRutime类执行的第一件事情是创建HttpContext对象,并用HttpWorkerRequest类进行初始化。HttpContext类是管道的“粘合剂”,因为它把与当前请求有关的所有信息保存在一个地方,把所有类合成一体。第一次创建HttpContext对象时,它分配HttpRequest和HttpResponse类的新实例并且字段存储它们。它还为应用程序和会话状态提供了专用的访问器。(关于HttpContext请参阅3.2节)
4、一旦创创建了HttpContext类,HttpRuntime类就通过调用HttpApplicationFactory类的静态GetApplicationInstance方法,为该应用程序请求HttpApplication派生类的一个实例。
5、GetApplicationInstance要么创建HttpApplication(或派生类)类的一个新实例,要么从应用程序对象池中拖出一个实例。(关于HttpApplication请参阅3.3节)

6、一旦创建或检索到HttpApplication类,就对它进行初始化,并在初始化期间分配为此应用程序定义的所有模块。模块是实现IHttpModule接口的类,它为进程前后的请求提供服务。
7、一旦创建了模块,HttpRuntime类通过调用它的BeginProcessRequest方法,要求最新检索到的HttpApplication类对当前请求提供服务。然后,为当前请求找到合适的处理程序工厂。
8、创建处理程序,传递当前HttpContext,一旦ProcessRequest方法返回,请求完成。

 

HttpContext

上下文(HttpContextAsp.net)是Asp.net管道中最重要的一个类,该类维护所有与请求有关的数据,并且可以为管道中的大多数元素所访问,在请求的生命周期里,上下文一直是有效的。并且可以通过静态的HttpContext.Current属性访问,正如它的名字暗示的那样,HttpContext对象表示当前活动请求的上下文,因为它包含了在请求生命周期里你会用到的所有必需对象的引用,如:Request,Response,Application,Server,Cache。在请求处理过程的任何时候,你都可以使用HttpContext.Current访问这些对象。记住HttpContext是你的朋友,在请求或者页面处理的不同阶段,如果你需要相关数据都可以使用它获取。

 

HttpApplication

HttpApplication主要用作是Asp.net管道的事件控制器,负责请求的传输,通过触发事件,通知应用程序正在发生的事情。HttpApplication本身并不知晓发送给Web程序的数据,它仅仅是个消息邮递者,只负责事件之间的通信。它触发事件,然后通过传递HttpContext对象,把信息发送给被调用的方法。Asp.net管道的事件按照顺序逐一触发事件,如下图所示:

HttpApplication事件流程

HttpApplication向外界提供的事件:

事件激活原因顺序
BeginRequest 收到新的请求 1
AuthenticateRequest 已经确立用户的安全身份 2
AuthorizeRequest 已经验证用户授权 3
ResolveRequestCache 在受权以后但是在调用处理程序之前,如果找到缓存入口,则被缓存模块用来绕过处理程序的执行 4
AcquireRequestState 加载会话状态 5
PreRequestHandlerExecute 请求发送到处理程序之前 6
PostRequestHandlerExecute 请求发送到处理程序之后 7
ReleaseRequestState 所有请求处理程序完成以后,被状态模块用来保存状态数据 8
UpdateRequestCache 处理程序执行以后,被缓存模块用来存储缓存中的响应 9
EndRequest 请求被处理以后 10
Disposed 正好在关闭应用程序之前  
Error 出现一个未经处理过的应用程序错误时  
PreSendRequestContent 内容发送到客户之前  
PreSendRequestHeaders HTTP标题发送到客户之前  

 

IHttpModule

using System;
using System.Web;

namespace Test
{
    publicclass MyModule : IHttpModule
    {
        publicvoid Dispose(){}
        publicvoid Init(HttpApplication app)
        {
            app.BeginRequest +=new EventHandler(this.Application_BeginRequest);
            app.EndRequest +=new EventHandler(this.Application_EndRequest);
        }
        protected void Application_BeginRequest(object sender, EventArgs e)
        {
            HttpApplication app = sender as HttpApplication;
            app.Context.Response.Write("This Is MyModule Application_BeginRequest .");
        }
        protectedvoid Application_EndRequest(object sender, EventArgs e)
        {
            HttpApplication app = sender as HttpApplication;
            app.Context.Response.Write("This Is MyModule Application_EndRequest .");
        }
    }
}

web.config

<httpModules>
<add name="myModule" type="Test.MyModule, Test"/> 
</httpModules>

 

IHttpHandler

using System;
using System.Web;

namespace Test
{
    publicclass MyHandler : IHttpHandler
    {
        publicvoid ProcessRequest(HttpContext cont)
        {
            cont.Response.Write("This Is MyHandler ."+"<br />");
        }
        publicbool IsReusable {
            get { returntrue; }
        }
    }
}

web.config

<httpHandlers>
<add verb="GET" path="*.aspx" type="Test.MyHandler, Test"/>
</httpHandlers>

从如上代码可以发现,实现一个HttpHandler看似非常简单,它仅仅包含一个ProcessRequest()方法和一个IsReusable属性。但真是如此简单吗?记住,WebForms和WebServices都是作为HttpHandler实现的,在这个看似简单的接口里,其实很多的实现过程被隐藏了,我们只是做了简单输出而以。但关键点是通过ProcessRequest()可以得到一个HttpContext对象的实例,并且明白这个单独的方法可以从开始到结束负责处理一个Web请求。
另外大家可能注意到了IHttpHandler接口支持一个名称为IsReusable的只读属性,用于指明是否可以安全的共享一个特定处理程序的实例。如果构建一个自定义处理程序,并且从此属性返回true,则在用处理程序的实例对请求提供服务时,Asp.net共享处理程序的实例。如果返回false,则每次服务请求时要创建处理程序的一个新实例。通常,处理程序共享与否并没有多大差别,因为CLR中的实例化机制和垃圾回收器是非常有效的,所以共享处理程序类并不会得到多大好处。需要考虑使用共享的一种情况是,需要耗费大量时间的处理程序,比如从数据库中进行检索。而Asp.net提供的标准处理程序从不使用处理程序共享。

吴剑 http://www.cnblogs.com/wu-jian

<全文完>

 

向作者捐赠 如果您觉得本文对您有所帮助,可用微信扫描左侧二维码向作者捐赠。您的支持是原创的源动力!

作者:吴剑
出处:http://www.cnblogs.com/wu-jian/
本文版权归作者所有,欢迎转载,但必需注明出处,并且在转载页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2010-04-19 11:40  吴 剑  阅读(8909)  评论(9编辑  收藏
@2010-2017 WuJian, All Rights Reserved.