【转】ASP.NET应用程序生命周期趣谈
这几天一直在看ASP.net应用程序生命周期,真是太难了,我理解起来费了劲了,但偏偏它又是那么重要,所以我希望能给大家带来一篇容易理解又好用的文章来帮助学习ASP.NET应用程序生命周期。这篇就是了。
当你访问博客园想看我的这篇文章的时候,这个请求就被博客园的Web SERVER(IIS)接收到了。博客园IIS看了一眼我的请求,“噢,是.aspx啊,给ASP.NET去处理吧,就把我这个请求给了ASP.NET, 并且说:“这个你来处理,你处理完了之后把HTML给我,我好给邱爽一个回复”。
ASP.NET收到IIS传递过来的请求后也没时间抱怨啊 就开始干活儿了。怎么干的呢?
第一,它先创建了一个Context对象,它就像个箱子,箱子当然是来装东西的啦,装什么呢?
第二,ASP.NET创建了一个Request对象,包含了IIS传递给它的所有信息(IIS传递过来的实际就是个Request嘛)。
第三,ASP.NET接着又创建了一个Response对象,用来装HTML的,也放进箱子(Context)
第四,然后,ASP.NET说,太累了,这活儿没个干,还是雇个人吧。就雇了个项目经理(HttpApplication对象),然后就把箱子 (Context)交给项目经理并且对它说,这里有我们收到的 Request,你需要做的就是把里面的Reponse填一下,具体怎么干你掂量着吧,就走了。
这个项目经理(HttpApplication对象)就想啊,凭啥活儿我干钱你们拿啊?不行,我得找俩苦力去,于是就有了:程序员 HttpModule和程序员HttpHandler,姑且就称他们为P_Module和 P_Handler吧,项目经理先找到了P_Module.
P_Module(HttpModule)非常的能干,它能够去查看 ASP.NET交给项目经理(HttpApplication对象)的箱子(Context),并且根据里面的东西做一些决定,比如安全啊 (FormsAuthenticationModule),状态啊(SessionStateModule )等等吧。在P_Module工作完成之后 (也许已经改变了箱子里(Context)的内容),项目经理开始找P_Handler来做填充Response的工作。现在招人难啊,找了好久也没招 到,好吧,找猎头(HttpHandler Factory)吧。猎头公司一看,“噢,要.aspx Handler啊",于是找来了一个天生就善于并且愿意处理页面的P_Handler(.aspx页面最终会编译成一个有继承机构的 IHttpHander),然后交给项目经理(HttpApplication)。项目经理看了一眼P_Handler之后,觉得还行,就把 P_Module处理过的箱子交给它并且说:"处理一下这个箱子里的东西,然后交给我"。
P_Handler是个天生的处理页面的牛人,它根据Request 对象里的东西是用了一招"乾坤大挪移",不知道怎么挪的,就挪出了HTML并塞进了Response对象中。P_Handler自信的笑了一声,把箱子交 还给了项目经理(HttpApplication对象)。项目经理从箱子里面把Response对象拿出来直接给了IIS,IIS又给了你了,你就看到这 篇文章了。
故事就是故事,故事就是故去的事,就是往事。那往事肯定就有遗漏的地方。那我们这个故事遗漏了哪些地方呢?
第一,IIS和ASP.NET之间的交互不是像我说的那么简单而直接的,中间还发生了很多事情。
第二,HttpModule,也就是我们的程序员P_Module, 它其实还能干很多事情,我们并没有去发掘。
第三,HttpHandler,也就是我们的程序员P_Handler,它的"乾坤大挪移"就是ProcessRequest方法,这里并没有详述到。
第四,。。。等我再想想再跟您聊。
希望这边小文能够帮助你更容易的理解ASP.NET生命周期,我会继续努力,争取以最简单明了的方式来speak out ASP.NET原理和运行机制。欢迎拍砖,谢谢。
ASP.NET应用程序生命周期趣谈(二)
在上回书开始的时候我们提到博客园的IIS看了一眼我的请求后就直接交给ASP.net去处理了,并且要求ASP.NET处理完之后返回HTML以供展示。
那么我们不仅要问:
1, IIS肯定是没有眼睛的啦,那它是怎么“看”的呢?
2, 在“看”到了.aspx的页面请求后又是如何把它交给ASP.NET的呢?如果不做任何处理那它的存在又有什么意义呢?
3, ASP.NET收到这个处理请求后又是如何做的呢?它是怎么创建Context对象又是如何“雇佣”项目经理HttpApplication对象的呢?
本文将就这些问题进行深入而简单的探讨。
当你点击了这篇文章的链接,在很短的一段时间内博客园的IIS就收到了你的请求。它要“看”了。正如我们知道的,它没有眼睛,所以它依靠 ISAPI来“看”请求的后缀。我们这次请求的是.ASPX文件。ISAPI是全称Inernet Server Application Programe Interface, 它就是IIS的眼睛和路由器,先看后缀然后分发给各个应用,我们可以通过访问IIS的站点的属性—》主目录—》配置 来查看它的路由映射。
我们发现,在.aspx extension对应的Executable Path里,真正处理asp.net应用程序的是aspnet_isapi.dll。可以清楚的看到,它还能处理ASAX,ASCX, ASHX, ASD,BROWER,CD等等等一堆啊,真是功能强大。
然而,在“看”的方法方式上,IIS5和IIS6有一些不同。
IIS5通过inetinfo.exe进程在TCP端口(默认是80)来“看”那些进来的Request。正如我们刚才看到的,如果这些 Request是需要aspnet_isapi.dll来处理,则aspnet_isapi.dll创建(不太确定worker process是不是aspnet_isapi.dll创建的,但是它们通过命名管道来交互)并持续监视一个aspnet_wp.exe进程,它就是 asp.net最重要的组件:worker process。几乎所有的工作都是在这个进程中完成,它在IIS6中被改名叫做w3wp.exe。
IIS6则通过内核模式中的HTTP.SYS来“看”那些进来的Request。HTTP.SYS把进来的Request发送到相应的 Application Pool(应用程序池)。应用程序池再把Request传递给aspnet_isapi来进行创建worker process的工作。IIS6中的worker process已经是w3wp.exe了。
接下来在最重要的worker process中发生了什么呢?项目经理HttpApplication又是怎么被雇来的呢?请听我慢慢道来。
ASPNET_ISAPI在创建了worker process加载了CLR完成了托管环境的布局后,就什么都不管啦。Worker process开始管理一切,它把所有的工作都交给了HttpRuntime。最后,是HttpRuntime雇佣了项目经理 HttpApplication。然后,HttpRuntime并不是什么工作都没有做,它已经通过配置文件创建了所有的HttpModule并填写在了 HttpApplication的“工作列表”中,项目经理HttpApplicaiton事根据这个列表来工作的。HttpRuntime也创建了 HttpContext这个箱子并交给了项目经理HttpApplication。乎!现在我们终于理解“asp.net创建了HttpContext” 这句话了吧。现在总结一下HttpRuntime都干了什么:
1, 打造了HttpContext这个箱子来存储Request和Response
2, 建立了工作列表HttpModule
3, 雇佣了项目经理HttpApplication并把箱子Context交给它,然后把工作列表作为效绩考核列表也交给他。
4, 等着返回结果
PS:在这个过程中,其实还有更详细的过程,但是我觉得那无助与我们理解真正的重要的东西,反而会带来更高的难度,所以也就没往上写。有兴趣的筒子们可以去微软网站搜索相关资源。
接下来,“项目经理”HttpApplication所作的事相信大家已经在前一篇文章中有所了解啦,什么?没看?那就赶紧去看看吧。
ASP.NET应用程序生命周期趣谈(三) HttpModule
在之前的文章中,我们提到过P_Module(HttpModule)这个能干的程序员哥们儿,它通过在项目经理 HttpApplication那里得到的授权,插手整个应用程序级别的事件处理。所有的HttpModule都要实现IHttpModule接口,那么 我们看IHttpModule的定义:
namespace System.Web { public interface IHttpModule { void Dispose(); void Init(HttpApplication context); } } |
可以看到,HttpModule 主要就做了两件事,一个就是大家都明白的释放资源Dispose(),另一个则是初始化。用什么初始化呢?当然是HttpApplication。刚才已 经说过,P_Module程序员是经过了项目经理HttpApplication的授权了的,这里我们就可以看到,初始化方法参数就是 HttpApplication对象,那么HttpModule又是怎么处理应用程序级别的事件的呢?且看:
我们可以看到,项目经理HttpApplication可是实实在在的放权啊,它非常的相信P_Module可以做好这些事情,所以在初始化方 法 Init(HttpApplication context)中,程序员P_Module可以注册很多事件,比如说常用的BeginRequest, EndRequest, AuthenticateRequest, AuthorizeRequest等等,还有一些其它的不常用的事件我们就不再赘述。总而言之,HttpModule强大到可以插手整个应用程序周期的所 有事件---因为得到了充分授权嘛。下面是注册BeginRequest事件示例代码:
public void Init(HttpApplication context) { context.EndRequest+= new EventHandler(context_EndRequest); } private void context_EndRequest(object sender, EventArgs e) { //do something when the request end } |
那么这里,我们要澄清一个问题:在ASP.net应用程序生命周期趣谈(一)中我们曾经在后面提到“P_Handler自信的笑了一声,把箱子 交还给了项目经理(HttpApplication对象)”,这是不完全准确的。事实上P_Module已经是一个非常高级的程序员,他在有必要的情况下 (注册了EndRequest事件)是要review中级程序员 P_Module的工作的,这个review的事件就是HttpApplication的EndRequest的事件。当然,有时候也不review就直 接交给HttpApplication了,所以我们得出结论:ASP.NET请求处理是基于管道模型的,request从管道这头进去,经过了 HttpModule的一些列处理,到那头儿的HttpHandler再处理,最后再经过管道原路返回。一次请求HttpHandler只能有一个,而 HttpModule却可以有若干个。(ps: --!真是废话,MSND上写的比谁都清楚)
总的来说,P_Module高级程序员主要就是从项目经理HttpApplication那得到授权(传入HttpApplication参 数)处理应用程序级别的一些重要事件,这些事件贯穿整个应用程序生命周期。我们经常会使用到ASP.NET自带的HttpModule,也有时会使用自定 义的HttpModule。ASP.NET自带的HttpModul在以下目录中可以找到:C:\Windows\Microsoft.NET \Framework\v2.0.50727 \CONFIG/Web.config
而我们自己编写的自定义HttpModule则写在我们的应用程序的Web.config文件中,也是节点下,与系统自动配置的并无不同:
可以看到,节点是在system.web下面的,HibernateUtil就是我曾经写过的一个 HttpModule,非常典型,大家可以参考。对与系统自带的HttpModule有很多,这里我就随便挑了一个SessionStateModule 来给大家做一个大致的分析,以便大家了解HttpModule如何工作。
简单说一下什么事 SessionState。SessionState就是ASP.net处理Session的一种机制,简单来说就是它通过管理Session的存储位 置,来优化Session的性能和安全性,避免浏览器禁用cookie时session丢失。ASP.NET允许Session存储在三个位 置:aspnet_wp.exe(它崩溃session就丢失);aspnet_state.exe(单独的 stateserver);sqlserver(存到数据库中)。关于SessionState的内容,请参阅其它相关文章,以后有机会我自己也会写写。
书归正传,那么我们通过反编译SessionStateModule,我们得到两行有用的代码:
public void Init(HttpApplication app) { SessionStateSection sessionState = RuntimeConfig.GetAppConfig().SessionState; //从config文件中读取SessionState配置 this.InitModuleFromConfig(app, sessionState); //初始化SessionStateModule需要处理的事件 //Other Code } |
第一行从config文件中读取了SessionState的配置(节点下面的内容,配置了怎么存储Session等信息),第二行则初始化了SessionStateModule需要处理的事件,我们再看看InitModuleFromModule干了什么:
private void InitModuleFromConfig(HttpApplication app, SessionStateSection config) { if (config.Mode != SessionStateMode.Off) { app.AddOnAcquireRequestStateAsync(new BeginEventHandler(this.BeginAcquireState), new EndEventHandler(this.EndAcquireState)); app.ReleaseRequestState += new EventHandler(this.OnReleaseState); app.EndRequest += new EventHandler(this.OnEndRequest); } } |
我们可以看到,第一行代码中,HttpApplication添加了OnAcquireRequestState的若干事件的处理方法,第二行 添加了当 SessionState释放时的处理方法,第三行则添加了request接触时的处理方法。SessionStateModule内部如何处理我们这里 不做详述,但是我们可以清晰的看到,SessionStateModule处理了应用程序级别的时间,包括SessionState的获得,释放和 Request的结束。我们没有发现它处理开始Request的事件,但我们确信它是有能力去做的,只是此种情形下并不需要那么做。下面按照执行顺序列出 HttpApplication的事件列表以供参考:
1. BeginRequest //开始 2. AuthenticateRequest //验证 3. AuthorizeRequest //授权 4. AcquireRequestState //获取request state 5. ReleaseRequestState //释放request state 6. UpdateRequestCache //更新cache 7. LogRequest. //这个事件只支持.net 3.0以上版本和IIS7的集成模式 8. EndRequest //结束 |
所有的HttpModule(包括自定义的和系统自带的)均有能力处理这些事件从而对应用程序产生影响,看来这个P_Module程序员果然很牛啊,项目经理信任它并授权也是有道理的。
然后P_Module作为一个高级程序员,它自身知识积累固然很强大,它的学习能力也是不可忽视的。它可以自己学习(创建)一些新的能力(自定 义Module),经典的例子就是它网上盛传的它能在每个页面中都加入一些文字;这里我只简单的说明我用过的一个P_Module自建的在 EndRequest事件中关闭 NHibernateSession的例子:
首先,我们创建一个类HibernateUtil,继承接口IHttpModule,并实现接口方法:
public class HibernateUtil : IHttpModule { public void Dispose() { } public void Init(HttpApplication context) { } } |
然后,注册EndRequest事件:
以下是代码片段: public void Init(HttpApplication context) { context.EndRequest+= new EventHandler(context_EndRequest); } private void context_EndRequest(object sender, EventArgs e) { CloseSession(); } |
最后,配置Web.config:
此时,我们就完成了一个自定义的HttpModule。这个小例子并不难,主要是说明了HttpModule的创建流程和方法,而本篇文章也难度也不是很高,希望对大家能有所帮助,欢迎拍砖,少扔鸡蛋,挺贵的,谢谢! ^_^