系列目录 【已更新最新开发文章,点击查看详细】
本章从IIS的历史介绍简述IIS的特性演进和IIS的架构,目的是使读者对IIS有一个初步的认识。让读者了解IIS是什么,能做什么以及IIS的组成部分。
1.1 IIS的前世今生
通过了解IIS的历史,读者可以理清IIS的发展脉络,更深入地了解IIS的特性。
1.1.1 IIS的历史
微软Internet信息服务(Internet Information Service,IIS),是在Windows平台上提供的一款微软官方的Web容器服务。打一个形象的比喻,lIS在Windows上的作用等同于Apachel服务在Linux服务器上的作用,缺少了llS,Windows操作系统就无法向用户发布Web网站。
IIS是一个支持HTTP协议和FTP协议的Web服务器。仅靠IIS本身是没办法直接运行ASP.NET或PHP等Web应用程序的,这点也和Apache类似,它需要有Tomcat的参与才能运行JSP技术的动态网站;IIS也需要ASP.NET平台的配合才能运行ASP.NET Web应用程序。
IIS作为一种操作系统内建的服务程序,是随着Windows操作系统一同发布的,每当有新版本的Windows)服务器操作系统发布,就会有新版本的lIS随之发布。从1993年第一个版本算起,至今已经历经12个版本,22年的时间,以下是IIS各个版本的演进简史:
- IIS1.0 是在1993年随着Windows NT3.5.1介质作为免费插件的形式发布的。
- IIS2.0 随着Windows NT4.0操作系统一同发布。
- IIS3.0 随着Windows NT4.0 Service Pack2介质发布,并首次支持了微软ASP技术(微软第一种动态页面技术),使得程序员可以开发动态页面服务。
- IIS4.0 随着Windows NT4.0 Option Pack发布,并为用户提供了IIS管理控制台程序,使得管理员可以可视化地管理。
- IIS5.0 随着Windows Server2000发布,支持了多种验证方式和WebDAV协议,并改进了ASP技术。
- IIS5.1 随着Windows XP Professional发布,功能与IIS5.0类似。
- IIS6.0 随着Windows Server2003和Windows XP Professional64位版本一同发布,对IIS的工作进程做了重大改进,支持了多种验证方式,并启用w3wp.exe作为Web网站工作进程容器。
- IIS7.0 随着Windows Vista和Windows Server2008发布,llS7.0是一个完全重新设计的版本,IIS7.0将IIS进行了组件化设计并极大地提升了IIS性能。
- IIS7.5 随着Windows7和Windows Server2008R2发布,把命令行工具改为PowerShell实现。
- IIS8.0 随着Windows8和Windows Server2012发布,支持NUMA架构(Non-Uniform Memory Access非均匀内存访问架构),并改进了应用程序池初始化的性能。
- IIS8.5 随着Windows8.1和Windows Server2012R2发布,增强了IIS日志功能,并首次支持了ETW(Eventing Tracing for Windows)日志,同时改进了应用程序池空闲和工作状态切换的性能。
- IIS10 随着Windows10和Windows Server2016发布,完美支持HTTP2.0。
1.1.2 IIS各个版本的重要演进
进入21世纪以来,IIS最主要的版本是IIS5.0到IIS8.5这几个版本。IIS从一个青葱少年变得日臻成熟,逐渐成为Web容器的主力军,在Web容器市场占有了较大份额。下面将归纳几个版本的重要演进。
1.持续改进的IIS工作进程
在IIS5.0以前的版本,Web网站的工作进程(Web网站也是一段程序,也需要在进程中运行)都是IIS自身服务程序进程Inetlnfo.exe。IIS管理服务代码和Web网站业务逻辑代码混在一起的弊端是:当有一方代码出现严重问题,例如指针越界错误,就会导致IIS服务和Web网站一起崩溃;另一个缺点是IIS服务管理代码占用了部分进程内的资源,使得Web网站代码申请资源会受到不同程度的限制。
IIS5.0版本对Web网站的工作进程作出了重大改进:把Web网站工作进程与IIS服务进程做了分离,使用DLLHost.exe作为Web网站代码的宿主进程,Inetlnfo.exe仅作为lIS管理服务代码的运行进程。但是DLLHost.exe进程是Windowst操作系统上通用的DLL宿主进程,并没有对Web网站的运行场景作出优化。
IIS6.0以后的版本将IIS工作进程改进为w3wp.exe,内含专门运行代码的线程和专门负责I/O的线程,该工作进程在以后的版本中被固定了下来。
2.配置文件增加可读性和移植性
早期的功能单一,配置项较少,配置文件仅用来保存简单的设置数据。随着功能的扩展,可配置项越来越多,并且IIS越来越多地需要在负载均衡群集内进行部署,这使得IIS配置文件内保存的配置项越来越多,并且需要支持在Web服务器之间互相移植。从IIS6.0起,IIS配置文件从二进制格式转变为Metabase.xml的XML格式文件。从IIS7.0起,XML格式的配置文件变成三个XML文件,并保存在单独的Config文件夹中。使用XML格式保存配置项便于管理员阅读和排错,更方便配置文件在Web服务器之间互相复制移植。
3.不断地提高Web网站的运行性能
IIS把网络I/O操作从用户态迁移到内核态,使用专门的w3wp.exe进程来运行Web网站,对64位计算的支持和对应用程序池对象的功能的改进等方面都是为了不断地提升Web网站在IIS上运行的性能。
4.组件化的设计和丰富的接口使IIS越来越开放
自IIS7.0起,IIS使用组件化设计并提供了丰富的扩展接口,这使得IIS变得越来越开放了。当今的IIS不仅能支持微软自己的ASP.NET动态页面技术,第三方也可以通过编写ISAPI扩展的形式支持其他的动态页面技术。目前已经可以运行在IIS上的有PHP、Python、ActivePerl、Ruby,甚至是JSP。
1.2 初识IIS
本节将带读者了解IIS的安装目录、关键文件夹以及重要的服务。
1.2.1 IIS应用程序文件夹
IIS服务是通过Windows服务器版操作系统上的服务器管理器添加Web角色的方式安装的。IIS服务在操作系统上安装完成后,会把程序文件都存放在 %systemroot%system32inetsrv 路径下,inetsrv即Internet的缩写。如果Web服务器的操作系统是64位的,那么在 %systemroot%SysWOW64inetsrv 目录下另有一份32位版本的IIS服务程序。
一般来说默认路径为:C:WindowsSystem32inetsrv 或者 C:WindowsSysWOW64inetsrv
inetsrv文件夹中的内容由以下几个部分构成。
1.IIS的配置文件和文件夹
在IIS6.0之前版本中,配置文件是使用一个二进制格式的文件进行保存的。在IIS6.0中,配置文件变成了Metabase.xml的XML格式的文件,保存在inetsrv文件夹根目录下。到了IIS7.0及以后的版本,配置文件由一个Metabase.xml变成了三个XML文件:administration.config、applicationHost.config和redirection.config,一并保存在inetsrv文件夹内的config子文件夹内。配置文件从二进制演化成XML格式,极大地方便了管理员对I配置的维护、备份和排错。
IIS10中的配置文件信息如下图
config子文件夹内还有schema子文件夹,该文件夹中的内容是用来保存配置文件的XML定义的。
2.IIS的命令行管理工具
IIS命令行工具,早期的IIS命令行管理工具采用VBScript脚本编写,那时候IIS的管理对象都是COM的,因此使用VBScripti调用非常方便,最著名的如 liisapp.vbs,管理员经常使用该脚本文件查看IIS的应用程序池和 w3wp.exe 进程ID的对应关系。到了IIS7.0,IIS管理工具统一变成了appcmd.exe,该命令行工具可以对网站、虚拟目录以及应用程序池进行增删核查操作,也可以通过该命令行工具给当前IIS指定配置文件。总之,管理员在IIS管理控制台上进行的常规操作,使用appcmd.exe都可以完成。在之后的IIS版本中,命令行管理工具均使用PowerShell进行了改写。
3.IIS的功能组件
在IIS7.0以后的版本中,IIS把现有的功能都进行了组件化的重构,因此可以在inetsrv目录下看到很多.dll文件,每个文件都担负着处理IIS服务中某个特定功能的责任,例如,custerr.dll对应IIS的错误页功能,loghttp.dll对应lIS日志记录功能等。
4.IIS管理控制界面 IIS管理控制界面工具主要是 IIS.msc 和 inetmgr.exe。IIS.msc 调用的是IIS管理控制插件(Snap-in)的配置文件,启动时微软管理控制台会按照msc文件的要求加载IIS管理控制界面。inetmgr.exe则是一个可执行文件版本的管理控制台,二者功能一致,没有任何区别。IIS在Windows操作系统上是一种比较简单的服务,没有单独的安装包,应用程序文件夹也只有inetsrv一个。
1.2.2 IIS Web 内容文件夹
对于Web网站的内容,IIS规划了一个名为inetpub的文件夹来存放。一般而言,该文件夹会存放于系统盘根目录下,这个文件夹内含有若干个子文件夹,每个子文件夹都有非常重要的作用
每个目录的作用描述如下:
除此之外,IIS的HTTP错误日志保存在 %systemroot%system32LogFilesHTTPERR 文件夹内。
出于管理的方便和性能的考虑,一般不会把log文件夹和wwwroot文件夹直接拿来使用,而是在别的磁盘分区存放IIS日志和Web站点的内容。
1.2.3 IIS相关的 Windows 服务
IIS安装后还会启动以下一些Windows服务用来保障Web站点的运行,每个IIS版本的Windows服务略有不同,以下是这些服务的简要介绍,如表1.2所示。
在操作系统的服务列表中可以找到上述服务
对于 Web Management Service 服务,如果管理员需要远程管理 IIS的服务,则手动开启;如果不需要,则关闭该服务。
FTP Publishing Service 与 FTP Publishing Service 6 需要在Windows功能面板中选中并安装后才会出现在服务列表中。如果不使用FTP相关功能,可以不安装。
1.3 关键进程 InetInfo.exe 和 w3wp.exe
IIS服务有两个进程最为关键:IIS服务自身的进程Inetinfo.exe或WAS服务进程,以及运行Web网站所使用的进程w3wp.exe。
Inetinfo.exe进程负责在IIS启动时加载IIS的配置文件Metabase.xml到内存中,并按照配置文件的描述启动和管理IIS的各个网站。IIS7.0以后,Inetlnfo.exe不复存在,它的功能被分散到几个IIS相关的Windows服务中。但是在最早期的IIS服务中,Inetlnfo.exe进程不但负责运行IIS服务程序,还负责运行Web网站。这样的设计会带来一个问题:Web网站的崩溃也会导致IIS服务的崩溃。为了增强IIS的鲁棒性(强壮和健壮性),势必要把IIS的服务进程和Web网站工作进程分离开,于是出现了使用DLLHost.exe作为Web网站的运行进程的方式。时间一长,人们发现DLLHost.exe作为运行微软动态链接库的壳程序,运行Web网站有一些天然的劣势,例如没有对Web网站的高I/O场景进行优化,DLLHost.exe安全性差,容易被注入等(很多病毒就是靠DLLHost.exe注入Windows操作系统的)。于是到了IIS6.0,为Web网站单独设计一款运行网站代码的壳程序就势在必行了。
以上所提到的壳程序的名字就是w3wp.exe,它是World Wide Web WorkerProcesst的缩写,即万维网工作进程。这款w3wp.exe进程自IIS6.0版本引入,专门针对Web网站的I/O场景做了优化,内含有专门运行代码的工作线程和专门处理数据包收发的工作线程,且异步处理HTTP请求的方式,极大地提高了Web网站代码运行效率。
在64位操作系统上,IIS同时提供了64位和32位两个版本的w3wp.exe进程。分别用来运行64位的网站代码和32位的网站代码。这是因为从Windows的设计上考虑,64位的进程是没办法直接运行32位的代码的,需要使用32位的进程才能加载针对32位编译的代码。
在IIS6.0中,两个进程的关系是:Inetinfo.exe进程负责启动、关闭和管理w3wp.exe进程。Inetinfo.exe进程是w3wp.exe的父进程,Inetinfo.exe通过向w3wp.exe进程定期发送数据包的方式感知w3wp.exe进程是否工作良好。在IIS7.0以后版本,对w3wp.exe进程的启动停止以及监控工作交给了Windows的WAS( Windows Process Activation Service,宿主程序:svchost.exe)服务。
在一个 w3wp.exe 工作进程中,一个HTTP请求通过多个顺序步骤,在Web服务器中被称为事件。在每个事件中,每个内建的模块处理HTTP请求的一部分,例如认证用户的合法性或将HTTP请求信息添加到事件日志中。如果某个步骤需要一个托管代码功能模块来处理如执行表单验证,内建的托管代码引擎就会创建一个应用程序域(AppDomain)来执行必要的处理,当请求顺序通过了所有功能组件的处理,HTTP请求的结果就会被返回到http.sys中发送给用户。
1.4 IIS架构探秘
本节将详细介绍HTTP请求是如何被IIS的各个组件顺序处理的。
1.4.1 IIS的内核层实现
早期的IIS是一个运行在用户态的服务程序,这和一般程序员自己编写的桌面程序没有本质的区别。应用程序都是运行在保护模式下的用户态,由操作系统为程序分配资源来运行。到了IIS6.0,为了进一步提高IIS的性能和数据吞吐量,最基础的I/O部分和协议处理部分被封装成了Windows的内核驱动,以内核驱动的方式在Windows上运行可以直接访问计算机的物理内存,程序运行更加高效。与IIS相关的内核驱动程序有两个:一个是tcp.sys,另一个是http.sys。
所谓TCP,是用来定义在网络上数据传送方式的协议,它是一个位于OSI七层协议栈的传输层的协议。因此,tcp.sys专司Windows操作系统与外界使用TCP协议传输数据的功能。
HTTP协议是一个定义在应用层的协议,它定义了数据交互的谓词数据的格式等等,但是传输层上是使用TCP协议进行数据包传送。了解以上内容有助于理解http.sys和tcp.sys的关系:tcp.sys位于Windows通信的最底层,凡是使用TCP协议传输的HTTP协议数据包都会被tcp.sys完成组包后再交给http.sys进行处理。
现在需要深入介绍http.sys的内部构造,因为它和IIS处理HTTP请求直接相关。
如图1.1所示的最底层是TCP/IP协议栈,由tcp.Sys负责处理。当请求的数据包包含一个HTTP请求时,就会由tcp.sys转给http.sys进行处理。http.sys中包含有HTTP引擎,专门用于对HTTP请求进行分析和解析,即从HTTP请求中分析出头部数据、查询串和域名等信息。对于合规的HTTP请求,http.Sys会生成HTTP上下文对象,并把上下文对象放到网站对应的请求队列中排队等待处理。
http.sys提供一组不开放给第三方的API以便于IIS的用户态的程序调用,用户态的程序可以通过这些API从等待队列中提取HTTP上下文对象并传递给工作线程进行处理。对于处理结果,用户态的程序会通过API把运算结果再交给http.sys,由http.sys的数据响应处理模块把数据发送给客户端浏览器。如果IIS管理员配置了缓存策略,那么这部分数据还会同时缓存到响应缓存模块中,以待下次接收到相同请求时,直接返回缓存中的数据。
以上就是http.sys的主要工作原理,IIS管理员可以通过修改配置的方式设定缓存策略和大小以及等待队列的长度(这些在后续章节都会有详细的介绍),但无法通过编程的方式控制http.sys的行为。
1.4.2 IIS的应用层实现
http.sys在内核态上处理完HTTP请求后,IIS就会把HTTP请求对应的HTTP上下文对象转到对应的应用程序进程中,由对应的w3wp.exe进程对请求进行处理。
其实IIS本身只能处理htm或html等静态HTML页面,对于动态页面,IIS自身是无能为力的。那么怎么让IIS能够支持诸如ASP.NET或PHP等动态页面技术呢?答案就是采用ISAPI。ISAPI可以被理解为是IIS的一种扩展插件,当IIS发现某种服务器上的资源自己无法处理时,就会按照配置信息把请求转给对应的IAPI的扩展来执行;IIS会等待ISAPI的执行结果,然后把结果透传给客户端浏览器。
如图1.2所示,IIS发现请求是一个自己无法处理的.aspx结尾的页面,于是会在w3wp.exe进程中按照预设配置创建ASP.NET ISAPI扩展的实例,让ISAPI计算好结果返回给用户。在Web Farm模式下,一个应用程序池会有多个 w3wp.exe 进程实例—起工作。
以上提到了IIS会按照预先配置调用对应的ISAPI,那么ISAPIE的配置在哪里呢?实际上它是在IIS的处理程序映射模块中进行配置的。如图下图所示,ASP.NET配置了一条策略,告诉lIS当遇到以.aspx为结尾的资源时,不要自己处理而是调用aspnet_isapi.dll这个ISAPI来处理。
可以发现,针对一种特定的资源类型,IIS的处理程序映射中有多条配置,虽然它们看起来是重复的,其实不然。以.aspx为例,如果Web服务器上同时运行着.NET4.0和.NET2.0那么就需要两条独立的配置;如果还需要同时兼容32位和64位,那么就需要四条.aspx的配置才够,因此处理程序映射的配置并没有重复。
1.4.3 一个HTTP请求在IIS上处理的完整流程
如图1.4所示,该图描述了一个HTTP请求的完整处理流程,通过该流程可以了解到IIS的各部分组件是如何协同工作的
①用户在客户端浏览器输入一个URL地址,向Web服务器发起HTTP请求,这个请求会首先被http.sys内核驱动进行处理。
②http.sys驱动联络WAS服务,从配置文件中获取网站相关的配置信息。
③WAS服务请求从配置文件中获取网站相关配置信息。
④服务接收到网站相关的诸如应用程序池和站点配置等配置信息。
⑤WWW服务使用的配置信息来配置http.sys内核驱动的行为,如请求队列等。
⑥当站点还没有工作进程为它服务时,WAS服务按照网站应用程序池配置启动一个w3wp.exe工作进程。
⑦工作进程处理用户的HTTP请求,并将结果返回到http.sys驱动作为对该HTTP请求的响应。
⑧客户端浏览器收到响应,渲染页面给用户查看。
1.4.4 一个ASP.NET 页面请求的处理
1.4.2节已经介绍了IIS是通过ISAPI扩展的方式来处理自己无法处理的动态页面请求的。本节会着重介绍当HTTP请求到达ASP.NET ISAPI之后具体发生了什么,如图1.5所示。
当ASP.NET ISAPI接收到某个站点的第一个请求的时候,会通过一个名为ApplicationManager的对象创建一个应用程序域(Application Domain)。应用程序域为Web应用程序的运行提供一个隔离空间,工作进程内允许每个单独应用程序域进行创建和卸载。在应用程序域内,会创建一个HostingEnvironment类型的对象,这个对象可以用来访问与应用程序相关的信息,如应用程序所在文件夹路径等信息。
当Web网站的应用程序域对象创建成功后,对每一个HTTP请求都会创建一个HttpContext核心对象,该核心对象中包含有HTTP请求相关的全部信息,因此它在内存中非常庞大,每个HttpContext对象会占据大约20~30KB的内存。在HttpContext对象中含有HttpRequest和HttpResponse对象,用于封装HTTP的请求和响应数据。
对于Web网站的全局对象如每个功能模块和Session等对象,都被封装在HttpApplication对象中。HttpContext和HttpApplication两个对象极大地方便了程序员的编程,他们可以在代码的任意位置访问这两个对象,以便获取相关数据。
HttpApplication对象还带有一些全局事件,方便程序员在HTTP请求经过特定功能模块时加入自定义的处理方法。这些事件的响应函数通常被存放在网站代码的Global.asax文件中,如常见的Application BeginRequest和Application EndRequest分别在HTTP请求到达和离开ASP.NET Web网站时触发。
值得一提的是,Global.asax文件中有两个特殊函数:Application_Start() 和 Application_End()。它们代表了Web应用程序的创建和退出,但是它们不是HttpApplication对象的事件响应函数。ASP.NET只在Web引用程序创建和退出时各调用这两个函数一次。 通过以上内容,读者可以了解到一个HTTP请求是如何被IIS内部的各种组件进行处理的,深入地了解HTTP请求处理流程有助于后面章节的学习。
原文:《微软互联网信息服务IIS最佳实践》