【翻译】深入解析Web服务器和ASP.NET应用程序生命周期
第一次翻译,另水平有限,难免出错,敬请原谅,如果实在难以理解,请看原文对照
原文链接:http://www.codeproject.com/Articles/121096/Web-Server-and-ASP-NET-Application-Life-Cycle-in-D
简介
在本文,我们将试着解释清楚当用户向ASP.NET应用程序发送一个请求时到底发生了什么。有许多文章已经解释过这个问题,但是没有一篇能够清晰而又深入的解析。在读完本文后,你将会明白:
- 什么是web 服务器?
- HTTP – TCP/IP 协议
- IIS
- 网络通信
- 应用程序管理器(Application Manager)
- 宿主环境(Hosting Environment)
- 应用程序池(Application Pool)
- 一个请求会创建多少应用程序域
- 一个请求会创建多少HttpApplication和怎样修改它的行为
- 什么是工作者进程和一个请求会运行多少个工作者进程
- 在请求和响应之间到底发生了什么
从零开始
我所读过的文章都是以“用户向IIS发送一个请求.....bla bla bla”。每个人知道IIS是一个web服务器,用来存放web应用程序的,但是web服务器又是什么呢?
让我们真正的正式开始吧。
web服务器(比如 IIS/Apache等等)是一个允许网站通过HTTP协议被访问的软件。我们能够抽象这个概念:web服务器是一个允许资源(web页面,图片,等等)能够通过HTTP协议访问的软件。
众所周知,网络通信有两个重要角色:客户端和服务器。
客户端和服务器之间相互通信需要通过连接和一些相互都能够理解的规则来实现。这些规则称为协议(protocols)。比如,当我们和某人说话时,就是使用了协议(语言)。通信协议定义了数据单元的格式和数据交换的规则。
HTTP协议知道所有的“语法”,但是它不知道如何打开连接或者发送数据,因此HTTP协议构建在TCP/IP协议之上。下图是TCP/IP模型
HTTP 是无连接的,但是并不意味着客户端和服务器之间通信之前不需要建立连接。意思是客户端和服务器开始通信之前不需要进行准备工作。
无连接意味着客户端不在乎服务器是否做好准备接受请求,服务器不在乎客户端是否做好准备获取响应,但是通信前建立连接时必需的。
在面向连接的通信中,通信节点一定要在数据交换之前建立一条物理或者逻辑数据通道/连接。
现在,让我们看看当用户在浏览器地址栏输入一个网站地址时发生了什么
浏览器把URL分为3部分:
- 协议类型("HTTP")
- 域名(www.Pelusoft.co.uk)
- 文件名(index.html)
浏览器与域名服务器通信把域名转换成对应的服务器IP地址
浏览器形成一个IP地址和端口80的连接
通过HTTP协议,浏览器向服务器发送一个GET请求,访问文件http://www.pelusoft.co.uk.com/index.htm。(注意cookies可能也会通过Get请求从浏览器发送到服务器)
服务器发送HTML文本到浏览器
浏览器解析HTML形成页面展示在你的屏幕
客户端发送一个请求(request)之前,客户端先要建立连接,在服务器发送响应(response)之后,服务器会断开连接。客户端和服务器通信过程中可能会因为用户操作,时间过期或者程序错误从而导致连接过早地断开,通信双方会不记录通信状态而选择直接终止当前的请求(HTTP的无状态性)。
IIS是一个监听特殊端口(通常是80)的进程。监听意味着它时刻准备着接收来自端口(80)的连接。记住:IIS不是ASP.NET。这意味着IIS与ASP.NET没有任何关系,它可以独立运行工作。我们有一个只存放HTML页面、图片或者其他网络资源的web服务器。web服务器,就像我上面所说,仅仅是返回浏览器请求的资源。
ASP.NET and IIS
web服务器也能支持服务器脚本(比如ASP.NET)。这个小节我将讲述在服务器上运行ASP.NET以及IIS怎样和ASP.NET引擎通信。当我们在服务器上安装ASP.NET的时候,安装程序会更新应用程序的脚本映射到相应的ISAPI扩展来处理IIS接收到的请求。例如,"aspx"扩展名会被映射到aspnet_isapi.dll,因此向IIS发送的.aspx文件的请求都会转交给aspnet_isapi处理(可以使用Aspnet_regiis来注册ASP.NET),脚本映射如下:
ISAPI筛选器是一个能够在IIS之前访问HTTP数据流的组件。没有ISAPI筛选器,IIS不能将请求转发给ASP.NET引擎(比如.aspx页面)。我们可以把ISAPI筛选器看作IIS请求的路由选择器:当请求的资源后缀名在脚本映射表中存在,ISAPI筛选器会将请求转发给对应的处理程序。假如请求的是.aspx页面。
当接收到请求:
- IIS创建/运行工作进程(w3wp.exe)
- aspnet_isapi.dll宿主在w3wp.exe进程中。IIS检查脚本映射并且将请求转发给aspnet_isapi.dll
- 请求被转交给.NET runtime(也宿主在w3wp.exe中)
最后,请求进入运行时
这段讲述运行时怎样处理请求以及阐述工作者进程中涉及到的所有对象。
首先,让我们看看请求进入运行时发生了什么
当ASP.NET接收到第一个请求时,ApplicationManager类创建一个应用程序域。应用程序域之间相互隔离,而且可以单独卸载 。
在应用程序域中,创建一个HostingEnvironment类的实例,这个实例能够访问应用程序相关的信息,比如存储应用程序的文件名称。
在应用程序域创建和HostingEnvironment对象被实例化之后,ASP.NET会创建并初始化核心对象比如HttpContext,HttpRequest和HttpResponse。
在所有核心应用程序对象初始化完之后,开始创建HttpApplication对象。
如果应用程序有Global.asax文件,ASP.NET还会创建自HttpApplication派生的Global.asax类的实例。
大部分文章都不会详细解释上述步骤,在本文,我们将深入分析每一步发生了什么。
Application Manager
我们第一个要讲的是Application Manager。
Application Manager位于所有运行着的ASP.NET 应用程序域之上,能够关闭它们或者监察空闲状态。
比如,当你修改web应用程序配置文件时,Application Manager负责重启应用程序域来加载新的配置文件使所有运行的应用程序对象重新创建。
那些已经在管道中的请求会继续运行,然而新的请求会进入新的应用程序域。为了避免“请求挂起”问题,ASP.NET会在请求超时之后强制关闭应用程序域,即使请求还挂着。
Application Manager是管理者,但是宿主环境包含了控制应用程序实例的所有“逻辑”。Application Manager 和 Hosting Environment就像接口和实现接口的类一样,Application Manager定义了行为,而Hosting Environment实现行为。
这时候,你应该有一个问题:Application Manager和Hosting Environment怎么通信的呢?因为宿主环境在应用程序域中,而且应用程序域有隔离性。实际上,宿主环境需要继承自MarshalByRefObject 远程和Applicaiton Manager通信。Application Manager 创建一个远程对象(宿主环境)然后就能调用其方法。
HttpApplication
HttpApplication 是web应用程序实例。它是一个负责解析请求并且返回响应的对象。一个HttpApplication一次只能解析一个请求。但是,为了获取最大化性能,HttpApplication实例能够被多个请求复用,但是一次只能处理一个请求。
你不恩能够手动的创建HttpApplication实例;因为那是有Application Manager负责的,你只能配置HttpApplication的最大数量。可以通过在machine config中配置来影响Application Manager的行为:
<processModel enable="true|false"
timeout="hrs:mins:secs|Infinite"
idleTimeout="hrs:mins:secs|Infinite"
shutdownTimeout="hrs:mins:secs|Infinite"
requestLimit="num|Infinite"
requestQueueLimit="num|Infinite"
restartQueueLimit="num|Infinite"
memoryLimit="percent"
webGarden="true|false"
cpuMask="num"
userName="<username>"
password="<secure password>"
logLevel="All|None|Errors"
clientConnectedCheck="hrs:mins:secs|Infinite"
comAuthenticationLevel="Default|None|Connect|Call|
Pkt|PktIntegrity|PktPrivacy"
comImpersonationLevel="Default|Anonymous|Identify|
Impersonate|Delegate"
responseDeadlockInterval="hrs:mins:secs|Infinite"
responseRestartDeadlockInterval="hrs:mins:secs|Infinite"
autoConfig="true|false"
maxWorkerThreads="num"
maxIoThreads="num"
minWorkerThreads="num"
minIoThreads="num"
serverErrorMessageFile=""
pingFrequency="Infinite"
pingTimeout="Infinite"
maxAppDomains="2000"
/>
通过设置maxWorkerThreads和minWorkerThreads,你可以设置HttpApplication的最大和最小数目。
为了处理一个web应用程序请求:
- 开启工作者进程w3wp.exe
- 创建ApplicationManager实例
- 创建应用程序池
- 创建宿主环境实例
- 创建HttpApplication
直到现在。我们只是讨论了一个web应用程序,如果有两个呢?
- 与上述相同的处理过程
- WebStie2将在已经存在的工作者进程中执行(两个站点需要是同一个应用程序池)
- WebSite2由同一个Applicaiton Manager实例管理
- WebSite2有自己的应用程序域和宿主环境
- 在新的应用程序域中运行着WebSite2的HttpApplicaiton实例
有一点非常重要:每个web应用程序运行在独立的应用程序域中,如果一个web应用程序出现错误就不会影响到其他web应用程序的执行,这时我们又有一个问题:
如果一个web应用程序(比如WebSite1)出错了而且影响到了工作者进程(w3wp.exe),会怎么样?
如果我想回收应用程序域会怎么样?
为了总结我所说的,一个应用程序池有一个或者多个进程组成。每个web应用程序都有一个应用程序域组成。问题是当你将多个web应用程序分配给同一个应用程序池,尽管它们属于不同而且相互隔离的应用程序域,但是它们仍然处在同一个进程(w3wp.exe),这与为每个web应用程序分配一个单独的应用程序池相比,安全性/可靠性较差。但是另一方面,它能够较少多个进程的开销而提高性能。
IIS应用程序池定义了许多web应用程序,能够同时分享一个或者多个工作者进程,它们提供了一个快捷的方式来管理web站点和应用程序以及它们对应的工作者进程。进程有独立性,因此,应用程序池中的web站点将不会收到其他应用程序池的应用程序的影响。应用程序池明显增加了web站点的可靠性和管理性。
更好的理解应用程序域和应用程序池的区别
为了更好的理解,假如你正在用IE浏览器,你能够在一个IE窗口打开多个IE标签页,假如你打开四个标签页浏览不同的网站
假设每个标签页运行在各自不同的应用程序域中,而且我们浏览的每个网站在在IIS下都是不同的web应用程序。我们浏览的所有网站都在同一个IE窗口(iexplorer.exe)的不同标签页。在这个例子中,我们把IE窗口(iexplorer.exe)比作ASP.NET工作者进程(w3wp.exe)。如果一个标签页由于某种原因换了,它将影响到同一窗口的其他标签页,所以你不得不强制关闭进程。
如果我们想将网站的浏览隔离在进程级别,一个网站出错,可以继续浏览其他网站,该怎么办?很简单,你可以打开两个IE窗口,每个IE窗口打开两个标签页。这种情况下,两个iexplore.exe进程运行,它们是完全相互独立的:如果一个IE窗口的某个标签页出错了,你关闭进程,但是不会影响到另外一个IE窗口。