IIS和Asp.Net页面运行机制
在.NET平台下进行Web开发主要运用的是Asp.Net框架,了解其运行机制更能让我们写出高质量的程序,修复不常见的bug。
最近想把这个用博客的形式记录下来。翻阅了一些书籍,浏览了一些大大们,如有雷同不是巧合,请见谅。
当我们在浏览器输入url地址请求时,浏览器会根据地址(DNS)找到你所请求的服务器,向服务器发送请求报文(Tcp/Udp)。
服务器(IIS)会根据的请求报文处理,如果是静态的网页(如html,js,css之类)就会在硬盘上找到相应的内容返回给浏览器。
如果是动态的网页(后缀是aspx,ashx,asmx等等)服务器就会根据不同的后缀就会映射给相应的程序处理。
首先什么是IIS(Internet Information Server,互联网信息服务)是一种WEB(网页)服务组件,其中包括Web服务器,Ftp服务器,NNTP服务器和SMTP服务器,
分别用于网页浏览、文件传输、新闻服务和邮件发送等方面,它使得在网络(包括互联网和局域网)上发布信息成了一件很容易的事。
IIS的各种版本
我们从IIS 5.x说起(貌似都是从这开始)
IIS 5.x
IIS运行在进程InetInfo.exe中,该进程寄宿在一个名为World Wide Web Publishing Service(简称 W3SVC)的Windows服务。
W3SVC的主要的功能包括Http请求监听、工作进程和配置管理(从Mrtabase中加载相关配置信息)等。
当服务器检测到Http请求时,先根据扩展名请求的是否是静态资源,如果是直接返回给浏览器。如果是动态资源,IIS会根据后缀映射给不同的程序处理。
例如,后缀是Aspx,IIS会根据Metadata database的信息映射给ISAPI(定义在一个动态链接库中)。Asp.Net ISAPI对应的dll文件名称为aspnet_isapi.dll。
当我们请求一个aspx类型的资源时,aspnet_isapi.dll会加载。而Asp.Net扩展会创建Asp.Net的工作进程(如果该进程未被启动)——aspnet_wp.exe
在aspnet_wp.exe工作进程中处理一系列的管道事件
IIS 6.0
IIS 5.x存在的不足
1.ISAPI动态链接库被加载到InetInfo.exe进程中,他和工作进程是一种跨进程的通信方式。
2.所有的Asp.Net运行在相同的进程(aspnet_wp.exe)中的不同的应用程序域。基于应用程序域的隔离不能从根本上解决一个应用程序对另一个程序的影响。
为了解决上诉两个问题,IIS 6.0将ISAPI动态链接库直接加载工作进程中;为了解决第二个问题,引入了应用程序池(Application Pool)的机制。
IIS 6.0的工作进程是w3wp.exe,另外还引入了Http.sys的Http监听器(一驱动的行式运行在Windows的内核模式(kernel Mode)),其配置信息没有保存在Metadata中,而是定义在注册表中。
与IIS 5.x不同,W3SVC从InetInfo进程脱离出来,运行在另一个进程中SvcHost.exe中,基本功能并未发生改变,只是在实现上改变了。
当Http.sys监听到用户的Http请求,将其分给W3SVC,W3SVC解析出请求的url,并根据从metadata获取的url与Web之间的映射关系得到应用目标,并得到应用目标运行的应用程序池或工作进 程.在工作进程中加载asp.net_isapi.dll。
IIS 7.0
IIS7.0 在请求的监听和分发机制上又进行了革新性的改进,主要体现在Windows进程激活服务(Windows Process Activation Service,WAS)的引入,将原来(IIS 6.0)W3SVC承载的部分功能分 流给了WAS。
上面从整体上介绍了IIS各个版本以及Asp.Net基本的框架流程。下面介绍Asp.Net管道的运行机制
Asp.Net 管道
如果Http.Sys接受到的Http请求是对该Web应用的第一次访问,在成功加载运行时后,会通过AppDomainFactory为该Web应用创建一个应用程序域,
随后一个特殊的运行时IsapiRuntime被加载。IsapiRuntime定义在程序集System.Web中,对应的命名空间为System.Web.Hosting,被加载的IsapiRuntime会接管Http请求。
在IsapiRunTime中执行的第一个方法是
public int ProcessRequest(IntPtr ecb, int iWRType)
在该方法中通过ISAPIWorkRequest的静态方法创建了一个ISAPIWorkerRequest继承自
HttpWorkerRequest (抽象类,封装浏览器发送的消息)
wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP);
然后将wr作为参数执行HttpRunTime的静态方法
HttpRuntime.ProcessRequestNoDemand(wr);
在ProcessRequestNoDemand先从请求队列获取一个请求,然后更新计数器
接下来调用 ProcessRequestNow(wr)方法
会看到_theRuntime.ProcessRequestInternal(wr)
_theRuntime就是RunTime类型的对象。
在ProcessRequestInternal方法里面会发现 HttpContext context;
context = new HttpContext(wr, false);
在HttpContext的Init的方法中初始化HttpResponse和HttpRequest
this.Init(new HttpRequest(wr, this), new HttpResponse(wr, this))
之后将context作为参数执行 this.EnsureFirstRequestInit(context)方法
在其方法里面调用FirstRequestInit(context)
完成配置文件的加载,初始化请求队列,装载Bin目录下所有程序集功能。
然后调用 context.Response.InitResponseWriter()
创建HttpWrite对象,用于写入结果返回信息。
接下来调用HttpApplicationFactory的GetApplicationInstance方法创建HttpApplication的实例。
IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance
HttpApplicationFactory用来负责管理HttpApplication对象
在GetApplicationInstance方法里面其实是调用
_theApplicationFactory.GetNormalApplicationInstance(context);
来创建默认的HttpApplication实例.在其方法里面执行InitInternal
他完成HttpApplication的初始化工作
首先初始化Modules(加载在web.config文件中配置的所有HttpModule模块)。
调用 this.InitModules();再其方法里面调用CreatModules利用反射创建HttpModules。
然后调用 this.HookupEventHandlersForApplicationAndModules(handlers);
完成global.asax文件中配置的HttpModule或HttpApplication事件的绑定。
接下来完成事件的绑定(19个管道事件)调用BuildSteps
this._stepManager.BuildSteps(this._resumeStepsWaitCallback);
在其中绑定用户处理请求对象Handler
steps.Add(new HttpApplication.CallHandlerExecutionStep(app));
获取HttpApplication实例之后如果实现了IHttpAsyncHandler接口HttpRuntime
开始调用其BeginProcessRequest方法
接着调用创建页面对象的ProcessRequest方法
applicationInstance.ProcessRequest(context);
19个管道事件
1.BeginRequest 开始处理请求
2.AuthenticateRequest 授权验证请求,获取用户授权信息
3.PostAuthenticateRequest 获取成功
4.AunthorizeRequest 授权,一般来检查用户是否获得权限
5.PostAuthorizeRequest 获得授权
6.ResolveRequestCache 获取页面缓存结果(如果没有则执行)
7.PostResolveRequestCache 已获取缓存
8.PostMapRequestHandler 创建页面对象
9.AcquireRequestState 获取Session
10.PostAcquireRequestState 获得Session
11.PreRequestHandlerExecute 准备执行页面对象.执行页面对象的ProcessRequest方法
12.PostRequestHandlerExecute 执行完页面对象了
13.ReleaseRequestState 释放请求状态
14.PostReleaseRequestState 已释放请求状态
15.UpdateReuqestCache 更新缓存
16.PostUpdateRequestCache 已更新缓存
17.LogRequest 日志记录
18.PostLogRequest 已完成日志
19.EndRequest 完成
当浏览器发送请求的时候,请求被处理需要用处理程序(必须实现了IHttpHandler接口或者IHttpAsyncHandler)来处理(在第八个管道处理程序PostMapRequestHandler 触发获得处理当前请求的
处理程序.,在第11个事件PreRequestHandlerExcute之后,HttpApplication将执行这个处理程序。
在第十一和二十个事件之间处理程序工厂SimpleHandlerFactory创建ashx(实现了IHttpHandler);
页面处理程序工厂PageHandlerFactory创建Page类(实现了IHttpHandler),通过反射,返回给HttpApplication完成请求的处理。
在PageHandlerFactory内部通过PageParser解析指定的aspx文件生成Page类的派生类,而这个派生类即用来创建页面处理程序对象实例。
http://www.cnblogs.com/SALIN/archive/2012/08/13/2636511.html
http://www.cnblogs.com/OceanEyes/archive/2012/08/13/aspnetEssential-1.html
http://www.cnblogs.com/artech/archive/2007/09/09/887528.html