Web服务器是不能直接处理动态服务的,而是通过应用服务器,这点毋庸置疑,当服务器发现自己处理不了JSP,ASPX等请求页面时候,会交给后面的应用服务器,对于ASPX.NET来说这个玩意就是aspnet_isapi.dll,说这些可能比较拗口,那么我就文绉绉一次。
Web服务器和应用服务器的概念经常可能混淆,比如在IIS下我们很少提到这个玩意,这是因为IIS将两个集成了,如果真的咬文嚼字,其实差别很大的,咬文嚼字不是为了抠字眼,也不是钻牛角尖,当我们不理解的时候或者有异议的时候如果能找到这种区别,这其实就是在做学问,咬文嚼字并不是什么坏事,但是有的时候会越来越弄不清,那么在这种情况下,就放弃吧,适可而止,可能是自己还没到理解的这一个步骤,如果继续纠缠,可能会让自己越发困惑。争取做一个头脑清晰,一清二楚的人,即便是向欧阳修这样的“醉人”,但是也要新不醉。
我们知道Web服务器只能处理理静态的页面,也就是我们常说的HTML,比如apache服务器,它就不能处理后缀名是JSP的页面,当客户端请求一个被请求的页面的时候(.html)结尾,如果服务器上不存在就会有伟大的404错误,如果有的话,那么就将这个页面呈献给浏览器,供浏览器解析,其结构图如下:
图也许是一些晦涩难懂原理的克星,书上的原理往往也都是用图来诠释, 一图胜千言此话不假,只要用得恰当,我欣赏这样的方法,我也喜欢,我也尽力在自己的博文中用这样的方法,可惜这些图都不是我自己做的,惭愧呀,都是在搜索引擎中搜索到的,既然自己做不了精品,那么就做一个不要错过精品的人,也做一个要有审视精品的眼光的人。不知大家看没看过oreilly出版的书,他们的书几何都是这种风格,买不起书的俺,只能收集一些pdf了。
应用服务器可以说是对Web服务器的扩展和Web服务器一同工作,上面说了Apache处理不了JSP,但是结合Tomcate就完美了,这里我找到了一张ASP的说明图,当ASP的请求。
用户将向浏览器输入对以.asp为扩展名的网页的请求;浏览器将会发送这个请求,并将其传递到适当的含有被请求页的Web服务器;Web服务器识别.asp扩展名,并把整个页传递到ASP应用程序服务器执行。ASP解释引擎将浏览这个页,并查找<% %>标签,它将这些标签转换成请求的HTML。一旦处理并转换了所有的ASP标签,整个页返回Web服务器,Web服务器将包装这个网页,现在只含有HTML标签,并作为响应发送到用户。其运行流程图如下:
这两图说明了Web服务器和应用服务器,有没有柳暗花明又一村的感觉,其实我在大学的时候根本就分不清这两个的区别,就认为是一样的,那个时候很少做Web的开发,也没深究,工作后经常听到这两个词语,于是下定决心将这个好好掌握下,在时间的洗涤下,终于豁然开朗,看来理解一个东西,是需要以时间为代价的,此话不假。只有清楚这些差别的人,写出来的东西,也容易让人接受,也许看的不是书,而是别人的心得。
那么一个HTTP连接到底做了什么?
在IIS中,我已经说了这两个不是很明显,因为微软将这些集成在一起了,不像apache和tomcat这么分明,但是我们要心中有数,知其差别。
应用服务器对动态的页面aspx,JSP,ASP等进行解析,这个过程好像一个黑匣子,对于热衷技术的我,如果不捅破这个匣子总感觉不痛快,脑子里面总会有几个为什么,在搜索引擎的帮助下,我获得了答案,现在只是的来源很多都是通过搜索引擎,感谢搜索引擎,下面就谈谈ASP.NET的整个处理流程(IIS7和IIS6也有区别,这里以IIS6为例)
下面的内容参考:http://www.hndxc.net/show.aspx?id=69275&cid=125
IIS 所收到的对某 Microsoft ASP.NET 页面的每个请求都被移交给 ASP.NET HTTP 管道。HTTP 管道由一系列托管对象组成,这些对象按顺序处理该请求,并完成从 URL 到普通 HTML 文本的转换。HTTP 管道的入口点是 HttpRuntime 类。ASP.NET 基础结构为辅助进程中所承载的每个 AppDomain 创建此类的一个实例请注意,该辅助进程为当前正在运行的每个 ASP.NET 应用程序维护一个不同的 AppDomain。
要激活 HTTP 管道,需要创建一个 HttpRuntime 类的新实例,然后调用其 ProcessRequest 方法。一个完整的页面请求会包括下面的流程:
首先被WWW服务器截获(inetinfo.exe进程), 该进程首先判断页面后缀, 然后根据IIS中配置决定调用具体的扩展程序。
aspx就会调用aspnet_isapi.dll, 然后由aspnet_isapi.dll发送给w3wp.exe(iis 工作者进程,IIS6.0中叫做 w3wq.exe,IIS5.0中叫做 aspnet_wp.exe)。
接下来在w3wp.exe调用.NET类库进行具体处理,顺序如下:
ISAPIRuntim, HttpRuntime, HttpApplicationFactory, HttpApplication, HttpModule, HttpHandlerFactory, HttpHandler
ISAPIRuntime:主要作用是调用一些非托管代码生成HttpWorkerRequest对象,HttpWorkerRequest对象包含当前请求的所有信息,然后传递给HttpRuntime
HttpRuntime:根据HttpWorkerRequest对象生成HttpContext,HttpContext包含request、response等属性, 再调用HttpApplicationFactory来生成IHttpHandler, 调用HttpApplication对象执行请求
HttpApplicationFactory: 生成一个HttpApplication对象 ,在HttpApplicationFactory创建HttpApplication之前,会查找config(web.config和Machine.config)文件中注册的所有的HttpModule,并根据配置信息加载相应的Assembly,通过Reflection创建对应的HttpModule,并将这些Module加到HttpApplication 的_moduleCollection Filed中。我们对一个Application的请求最终会落到一个HttpApplication对象上。当一个请求到来时,ASP.NET会在Httplication Pool中查找未被使用的HttpApplication对象。
HttpApplication:进行HttpModule的初始化,HttpApplication创建针对此Http请求的 HttpContext对象。应用程序的第一个请求到达时,工厂类提取有关应用程序类型的信息(global.asax 类)、设置用于监视更改的文件、创建应用程序状态并触发 Application_OnStart 事件。工厂类从池中获取一个 HttpApplication 实例,并将要处理的请求放入实例中。如果没有可用的对象,则创建一个新的 HttpApplication 对象。要创建 HttpApplication 对象,需要先完成 global.asax 应用程序文件的编译。(反射技术)
HttpModule: 当一个HTTP请求到达HttpModule时,整个ASP.NET Framework系统还并没有对这个HTTP请求做任何处理,也就是说此时对于HTTP请求来讲,HttpModule是一个HTTP请求的“必经之路”,所以可以在这个HTTP请求传递到真正的请求处理中心(HttpHandler)之前附加一些需要的信息在这个HTTP请求信息之上,或者针对截获的这个HTTP请求信息作一些额外的工作,或者在某些情况下干脆终止满足一些条件的HTTP请求,从而可以起到一个Filter过滤器的作用。
HttpHandlerFactory:把用户request 转发到HttpHandlerFactory,再由HttpHandlerFactory实例化HttpHandler对象来相应request
HttpHandler:Http处理程序,处理页面请求,HttpHandler是HTTP管道的终点,它为每个request生成输出。
System.Web.UI.Page就是这样一个典型的Httphandler,当我们请求一个aspx页面,这个HttpHandler就生成html发送回客户端。看Page类的签名:
public class Page : TemplateControl, IHttpHandler
{ }
可以看到,Page类就是一个IHttpHandler。
此时已经得到了Page面这个IHttpHandler,然后调用它的ProcessRequest方法,这个时候IIS的请求结束,页面的生命周期开始
这个过程也可以用图来解释(来自:http://www.cnblogs.com/xhwy/archive/2012/05/20/2510178.html):
小结:当客户端向服务器发送资源请求时,请求首先到达IIS的HTTP.SYS。然后HTTP.SYS发送请求道对应的Application Pool。 然后Application Pool发送请求到Worker Process(W3WP.exe)中加载ISAPI Extension,ISAPI创建一个HttpRuntime对象来通过HttpModule和HttpHandler处理请求。 然后页面生命周期就开始了。在这些过程中大量运用反射,我们也可以体验反射带来的灵活性(一般有配置文件配置的话,都是可以通过反射)
我们可以用步骤将一个完整的HTTP请求在ASP.NET Framework的处理过程表示如下:
HttpRequest ——>HTTP.SYS- Application pool ——> ASPNET_ISAPI.dll ——> Http Pipeline ——> w3wp.exe ——> HttpRuntime ——>HttpContext--> HttpApplication Factory ——> HttpApplication ——> HttpModule ——> HttpHandler Factory ——> HttpHandler ——> HttpHandler.ProcessRequest()(这个是自己的理解,写的,不对的话请指出)
页面初始化(Init): 服务器创建服务器控件的实例
加载(load): 控件实例被加载到它定义的页面对象中
预输出:(PreRender) 对控件的更改被更新,准备输出。
保存(SaveViewState): 控件的状态信息被保存。输出页面(Render):服务器为控件创建html标记。
处理(Dispose): 主要做的工作就是dispose, 关闭数据库连接,文件资源的释放等。
卸载(Unload):销毁服务器控件的实例
PreInit:
IsPostBack 属性
Init: 这个事件发生在所有的控件被初始化,所有的皮肤设置被应用以后。它用来读取或者初始化控件属性。它能够用来注册一些aspx页面中没有指出的控件的事件。 InitComplete: Use this event for processing tasks that require all initialization to be complete.
PreLoad: 加载页面的ViewState和所有的控件,然后处理所有的包含在Request实例中的postback数据。
Load: 这个事件可能是大家最熟悉的了。需要注意的是,Page对象会递归的调用子控件的onload事件直到页面和所有的子控件被加载完成。这个事件主要用来设置控件属性的值,建立数据库连接(通常不这么做)。
Control events: 这个就不多说了,主要是处理控件的事件,例如click。这也就让我们明白了每次我们click一个Button的时候,实际上是要先去执行load事件然后才执行click事件的,一般我们用!IsPostBack来判断一下从而避免执行不必要的加载逻辑。
LoadComplete: 页面所有的控件都被加载以后执行,暂时没有想到用来干什么。。。
PreRender: 在HTML被生成之前这是最后一个事件。每一个页面中的控件都有PreRender的过程。在这里对将要输出的HTML结果进行最后一次修改。
SaveStateComplete: 在这个事件发生之前,已经保存了所有控件和页面的,任何对page或者控件的改动都不会产生左右。暂时没想到用来干啥。
Render: 它不是一个事件而是一个方法。工作就是把HTML写回客户端浏览器。
UnLoad: 页面中的每一个控件都会发生这件事。在控件中,使用这个事件来做清理工作,例如关闭数据库连接等。对与页面本身也是做清理工作,例如关闭打开的文件和数据库连接,或者结束日志或者其它指定的工作。
需要说明的是,每次Request都会创建一个全新的Page类的实例,所以在页面中的自己定义的字段是不能在两次request中传递值的,需要使用viewstate来存储。
5, HttpHandler根据页面生命周期中事件的处理把结果发回IIS,IIS再把结果发回客户端浏览器。
值得注意的是,在这个过程中请求会再次通过HttpModule(注册一个EndRequest事件)。
来两幅图对这个页面初始化过程进行说明吧(http://www.cnblogs.com/cuihongyu3503319/archive/2012/08/25/2656683.html)
看到这幅图,我对博主的技术功底真是羡慕(http://www.cnblogs.com/xhwy/archive/2012/05/20/2510178.html)
写这篇博客,花费了不少时间,看了不少资料,感谢那些分享的人,
参考资料: http://www.cnblogs.com/GodSpeed/archive/2010/06/19/1761095.html
http://www.cnblogs.com/Mac_Hui/archive/2010/06/14/1758092.html
http://www.cnblogs.com/willpower/archive/2008/08/02/1258845.html
http://blog.csdn.net/dxfswpi/article/details/5935285
http://www.cnblogs.com/whtydn/archive/2009/10/16/1584418.html
http://www.cnblogs.com/Mac_Hui/archive/2010/06/14/1758092.html
http://www.tracefact.net/Asp-Net-Architecture/Http-Request-Processing-Flow.aspx
http://blog.csdn.net/bitfan/article/details/3013391