一些基本概念
//在一定的内存中承载应用程序,一个进程的错误可能造成其它进程的崩溃
2.应用程序域(AppDomain)
//Net程序需要Clr进行托管以保障安全,AppDomain正是Clr创建的区块,Clr利用进程,将其划分为N块逻辑分区
//这些分区就称为AppDomain,一个进程可以被划分成N个AppDomain。
//而.Net程序集通过CLR被载入到某个AppDomain中进行处理,也即AppDomain的作用是用来承载.Net应用程序的,其优势在于以下几点:
*.内存开销
//一个AppDomain的内存开销显著低于进程,而且跨域访问对象的内存开销也要显著低于跨进程访问对象
*.内存控制
//在C#中AppDomain被表示为一个类型,它提供了卸载程序集的方法,
//而Clr正是利用AppDomain进行垃圾回收和对程序集的卸载,开发人员也可以利用AppDomain对.net程序进行内存控制
//一旦程序超载则可以手动AppDomain卸载程序集,卸载后再将程序集重新载入以便降低内存占用。
*.热插拔
//在C#中AppDomain被表示为一个类型,它提供了加载程序集的方法
//把应用程序的某个分离的功能做成一个即插即用的插件,可随时应对客户需求的变更。
*.边界隔离
//一个进程的错误可能带来其它进程的崩溃,
//比如一台服务器开10个进程挂上10个网站应用程序,一个错误就会导致10个网站全军覆没,
//而AppDomain是进程的分区,域的边界隔离机制可以将多个.Net程序隔离,使它们相互独立,
//也即把每个网站程序放在一个进程的10个域中,一个AppDomain出错永远不会影响到其它AppDomain,
//从而不会导致整个进程的崩溃。但可能多个域都需要引用某个公用的程序集,最典型的例子就是MSCorLib.dll。
//该程序集包含了基元类型等重要的.net类型,像这样的程序集不需要跨域访问,
//因为在CLR初始化时就会将这种程序集自动载入一个公共域中,这样,所有的AppDomain都共享该程序集的类型。
3.HTTP通信接口( HTTP.SYS )
//Windows的核心组件,任何基于Windows平台的应用程序都要通过它提供的API接口才能进行HTTP通信
4,应用程序池( Application Pools )
//提供给开发人员管理应用程序的工具,控制应用程序的运行,结束,内存分配,请求最大链接数等
5.工作者进程( w3wp.exe )
//Windows的进程,与应用程序池是相互为用的关系,池管理应用程序,
//而w3wp.exe承载应用程序的运行。两者相互协作,控制应用程序的生命。
//工作者进程下可以划分多个AppDomain域来承载多个web应用程序。
//既然工作者进程总是关联一个应用程序池,
//所以你也可以这样理解:程序池是一个进程,该进程从Cpu中划分了一定的内存,web程序就被限制在程序池中,一个池子可以承载多个web程序的运行。
HTTP请求的整个流程
HTTP请求最早由HTTP.SYS接收,接着传递给IIS,请求进入IIS后,IIS会测试该请求所获取的是静态还是动态资源,而早期的web总是以静态的html资源返回给客户端,后来逐渐出现了动态资源,而IIS无法处理这种请求,所以需要一些扩展,通过把请求传递给扩展程序,由扩展程序去处理请求。
静态资源
IIS直接取出html,jpg等资源后往客户端发送。
CGI资源
基于CGI协议的早期通用网关接口,可处理扩展名为cgi的动态资源的请求,如果扩展名为CGI,则IIS会开启cgi.exe进程,执行由c,vb,python,php编写的可执行文件对请求进行处理。
扩展程序
通过IIS提供的配置,可以将扩展程序注册到由IIS管理的扩展程序映射表中,非静态资源的请求进入IIS后,IIS将查找扩展程序映射表,为了便于理解,可以视此表为键值对集合,key是动态资源的扩展名,value是处理请求的扩展程序。处理HTTP请求的常见扩展程序则有以下几种:
1.asp.dll扩展程序,执行由asp编写的可执行文件对请求进行处理。
2.FASTCGI扩展程序,这是CGI进化版的扩展程序,执行由c,vb,python,php编写的可执行文件对请求进行处理。
3.aspnet_isapi.dll扩展程序,可处理各种动态资源的请求,最强。
搭建Clr运行时
IIS确认后缀名所对应的扩展程序后则会通知应用程序池,后者通知w3wp.exe处理Http请求,而w3wp.exe进程会先确认CLR是否已经在运行,如果没有,则将aspnet_isapi.dll载入内存,由aspnet_isapi.dll负责搭建CLR(.Net运行时)。我们知道asp.net的程序集必须通过CLR的即时编译才能转化为cpu指令,才能够处理来自客户端请求的动态资源,所以工作者进程首先得把Clr运行时搭建出来。
创建应用程序域
Clr运行时搭建完成后,将开始创建应用程序域,( 此处需要注意,应用程序域使用的是延迟初始化机制,也即只有当第一个请求进入IIS并被有效传递给工作者进程后,域才会被创建。假如你手动关闭域以此来减轻内存开销,又或者遭遇不可控的因素致使域被关闭,那么当下一个请求被IIS传递给工作者进程后,域又会被重新初始化)。由Clr调用System.Web.Hosting命名空间下的AppDomainFactory的Create方法(.Net.20时代是调用AppManagerAppDomainFactory.Create)在当前工作者进程中创建出AppDomain对象和ISAPIRuntime对象,域创建完成后,工作者进程将把HTTP请求传递给处于域中的ISAPIRuntime对象,而该对象会调用自身的ProcessRequest方法,该方法根据原始的Http报文创建一个从HttpWorkerRequest派生的IsapiWorkerRequest对象,此对象就代表了Http请求的报文信息,接着该方法还会调用HttpRuntime的ProcessRequestNoDemand方法,该方法会调用其它方法,后续一系列的方法调用的逻辑都是为了能创建出HttpContext、HttpContext.Response、HttpContext.Request、HttpApplication、HttpModule和执行Http请求的管道事件,这些方法的部分源码如下:
internal static void ProcessRequestNoDemand( HttpWorkerRequest wr )
{
//RequestQueue是存储IsapiWorkerRequest的队列集合
//获取Http请求报文的队列集合
RequestQueue queue = _theRuntime._requestQueue;
//更新Http请求报文计数器,这将当前的Http请求报文添加到了集合中(个人臆测)
wr.UpdateInitialCounters( );
if (queue != null)
{
//再从队列中获取当前的Http请求报文
wr = queue.GetRequestToExecute( wr );
}
//ProcessRequestNow方法内部会将IsapiWorkerRequest参数传递给ProcessRequestInternal方法
ProcessRequestNow( wr );
}
//HttpRuntime.ProcessRequestInternal的部分源码
private void ProcessRequestInternal( HttpWorkerRequest wr )
{
HttpContext context;
//根据IsapiWorkerRequest创建HttpContext对象
//而在HttpContext的构造函数中会创建出Request和Response的实例
try { context = new HttpContext( wr, false ); }
catch
{
try
{
//400错误,请求无效
wr.SendStatus( 400, "Bad Request" );
//将错误信息输出
wr.SendKnownResponseHeader( 12, "text/html; charset=utf-8" );
byte[] data = Encoding.ASCII.GetBytes( "<html><body>Bad Request</body></html>" );
wr.SendResponseFromMemory( data, data.Length );
wr.FlushResponse( true );
wr.EndOfRequest( );
return;
}
}
//GetApplicationInstance方法内部会调用GetNormalApplicationInstance方法
IHttpHandler applicationInstance = HttpApplicationFactory.GetApplicationInstance( context );
if (applicationInstance is IHttpAsyncHandler)
{
//依次执行处理Http请求的管道事件
handler2.BeginProcessRequest( context, this._handlerCompletionCallback, context );
}
}
//HttpApplicationFactory.GetNormalApplicationInstance的部分源码
//此方法的作用:创建HttpApplication对象
private HttpApplication GetNormalApplicationInstance( HttpContext context )
{
HttpContext state = null;
//如果HttpApplication池存在处于闲置状态的HttpApplication则取出一个用于处理请求
if (this._numFreeAppInstances > 0)
{
state = (HttpApplication)this._freeList.Pop( );
//更新HttpApplication计数器
this._numFreeAppInstances--;
}
//如果池中没有闲置的HttpApplication,则重新创建一个HttpApplication
else
{
state = (HttpApplication)HttpRuntime.CreateNonPublicInstance( this._theApplicationType );
}
//InitInternal方法内部会调用InitModules方法
state.InitInternal( context, this._state, this._eventHandlerMethods );
return state;
}
//HttpApplicationFactory.InitModules的部分源码
private void InitModules( )
{
//读取注册在Web.config配置文件中的所有HttpModule节信息
HttpModuleCollection modules = RuntimeConfig.GetAppConfig( ).HttpModules.CreateModules( );
//HttpModule集合作为HttpApplicationFactory._moduleCollection的成员被使用
this._moduleCollection = modules;
//初始化所有HttpModule对象
this.InitModulesCommon( );
//注册ASP.NET的19个用于处理Http请求的管道事件
this._stepManager.BuildSteps( this._resumeStepsWaitCallback );
}
关闭域:httpruntime.unloadappdomain
客户端请求的动态资源的url原本是类似于以下形式
HTTP://Server_name/ISAPI.dll/File_name.aspx
经过IIS的优化,才变成了我们熟悉的方式
HTTP://Server_name/File_name.aspx
未证实
待续……
Visual Studio包含ISAPI向导加快开发