写在前面
一直都想去学习一下asp.net程序到底是怎样运行的,因为很多时候了解底层的东西能帮助我们更好地掌握一个技术。终于在上周静下心来google了不少博客资料,跟着博主们的思路和视角一步一步了解了IIS6中asp.net是怎样运行的(但远远不能称得上深入到底层)。另外,我觉得很多时候,要搞懂一个知识点,通常都需要不同的资料,追随不同作者的思路或者说视角,这样可能更容易地搞懂这个知识点。
另一方面,因为现在多数都是用IIS6,IIS7在核心方面跟IIS6没多大变化,所以我看的资料都是针对IIS6的,IIS5因为有点“过时”了,而且微软对IIS6针对IIS5的一些不足(主要是性能和可用性两方面)作出了较大的改进,所以本篇博客说描述的都是针对IIS6。
开始
先上图吧:
[1]:在windows server 2003中,加入了一个http.sys核心组件,用来监听HTTP通信。在以往,http的通信是由iis来负责的,因为IIS是一个web server,由它来监听http通信看上去合情合理。但由于iis是运行在“用户模式”下,不能访问到一些系统资源,在引入http.sys组件之前的iis(例如iis5)肩负着监控http请求和使用命名管道(named pipe)与运行asp.net的工作者进程(iis5中叫aspnet_wp.exe)通信,这种方式并不能获取最佳的性能,而http.sys作为一个系统驱动组件,运行在“内核模式”下运行,能直接访问到系统资源,而且能以比命名管道更高效的方式去工作者进程(在IIS6中改名叫"w3wp.exe")通信,所以IIS6性能要比IIS5更高。另一方面,因为http.sys已经不是IIS里的组成部分了,从IIS中独立出来,所以就算IIS进程崩溃,http.sys仍能继续监控http请求。
[2]:当http.sys接收到http请求时候,它将根据iis6中的metadata确定该使用哪个应用程序池来处理该http请求。在IIS5中,只有一个工作者进程来运行asp.net程序,不同的asp.net程序运行在该工作者程序中的不同应用程序域(application domain)中以确保各程序的独立性。但缺点也很明显:如果该工作者进程崩溃,会导致所有asp.net程序都挂掉,即使它们在不同的app domain中。而IIS6中是将不同的asp.net程序放到不同的工作者进程中(我们经常看见任务管理器中会有多个w3wp.exe),这样就能最大限度避免一个程序异常导致进程崩溃进而牵连到其他程序失败的情况。一个应用程序池默认只有一个工作者进程(可以在IIS中配置),但仍然保留app domain的概念。当然,IIS6可以管理多个应用程序池。
[3]:当http传递到w3wp.exe后,w3wp.exe会根据请求调用相对应的isapi,对于asp.net的请求,当然就是调用aspnet_isapi.dll了。这一点也是IIS6中对比IIS5改进的一点。在IIS5中,当IIS接收到http请求后,调用相对应的isapi,再进而启用(或调用)工作者进程,而在IIS6中,是工作者进程接收到http请求,再新建(或调用)工作者进程。
[4]:当http请求进入到aspnet_isapi.dll后,意味着正式启动.net对请求作出处理了。因为IIS6和w3wp.exe都是运行在非托管代码环境,而asp.net程序是在托管代码环境中运行,在托管代码环境和非托管代码环境中的通信就交由ECB来完成。
[5]:isapi runtime类负责加载clr,以运行asp.net程序,并将http请求封装成一个WorkerRquest对象。不过这个对象具体的存在意义,我尚未搞清楚~
[6]:http请求被封装成WorkerRequest对象后,进一步传递到http runtime类的一个对象中进行处理。HTTPRuntime对象的作用就是将WorkerRequest进一步处理成HttpContext对象。HttpContext对象拥有着http请求的所有数据,包括请求地址,主机端口,cookie,session等等。
[7]:其实asp.net程序就是http请求的加工工厂:根据客户的http请求(请求哪个页面,有什么参数等等),加工(或者说生产)出html内容。而http请求(在这里,这个http请求已经被封装成httpcontext对象)就是原材料。现在原材料已经有了,就需要一个可以对原材料进行加工的地方了,这个地方就是HttpApplication类了。HttpApplication类控制着怎样对HttpContext进行处理的逻辑。
[8]:但是,HttpApplication类并不是真正对http请求进行加工控制的类,它只是提供地方。真正处理http请求的,是被称为asp.net管线链(asp.net pipeline)逻辑。asp.net管线链由各种http module模块和一个http handler组成。http请求在进入asp.net管线后会触发各种不同的事件,例如BeginRequest,OnError,EndRequest等等。asp.net通过在不同事件中调用不同模块(例如caching模块,session模块)对http请求进行不同的处理操作。这也意味着:我们可以根据我们的实际需要编写Module来对http请求进行额外的控制或处理,典型的案例有:url_rewriter和图片防盗链。url_rewriter的原理就是:在http请求进入asp.net管线并触发begin request事件后,对请求地址进行转换,根据设定好的方式转换成实际请求地址,而防盗链则是在在http请求进入asp.net管线并触发begin request事件后,检查图片请求的url referer,判断对图片的请求是不是跨域的,如果是则将请求转向到防盗链图片的地址。
[9]:当处理通过了http modules的层层处理后,终于来到http handler类了,http handler是真正对http请求作出业务响应的地方,而一般的http handler就是我们的aspx和cs文件中的类,成为页面类,页面类都继承了Page类,而Page类这继承了IhttpHandler接口。在这里,我们编写代码,对http请求进行处理,例如查询数据库,对数据进行处理并生成table标签,最终输出html到客户端。而通常,一个asp.net程序可以有多个http module来对请求进行控制,但只有一个http handler处理请求。