您好,欢迎访问三七文档
以下为《构建高性能Web站点》部分内容节选,感谢您的阅读,您可以自由传播它,但请确保它的完整性。《构建高性能Web站点》目前还在编写中,预计上市时间为2009年9月,出版商为电子工业出版社。在本书上市之前,作者希望听到更多的建议和支持,如果您希望了解本书编写进展、试读内容节选、反馈宝贵建议,请您加入以下讨论组,对于讨论组活跃的成员,新书上市后会获得赠送版,以表感谢。《构建高性能Web站点》读者讨论组截至目前完成的内容索引列表《构建高性能web站点》已完成内容索引服务器并发处理能力服务器并发处理能力I/O模型模型有人说,比特天生就是用来被复制的,数据的生命意义便在于输入输出,的确,计算机的重要工作之一便是负责各种设备的数据输入输出,也就是I/O(In/Out)操作。事实上,I/O操作根据设备的不同,有很多种类型,比如内存I/O、网络I/O、磁盘I/O。对于内存I/O,一般我们在讨论Web站点性能时很少提及,因为相比于后两种I/O操作,内存I/O的速度已经足够快了,前边我们介绍过网络数据传输中的瓶颈往往在于带宽最低的交换节点,类似的,计算机性能的瓶颈往往并不在于内存I/O本身。对于网络I/O和磁盘I/O,它们的速度要慢很多,尽管使用RAID磁盘阵列可以通过并行磁盘访问来加快磁盘I/O速度,购买大量独享网络带宽以及使用高带宽网络适配器可以提高网络I/O的速度。可是,问题在于,这些I/O操作需要由内核系统调用来完成,同时系统调用显然需要由CPU来调度,而CPU的速度毫无疑问是非常快的,这就使得CPU不得不浪费宝贵的时间来等待慢速I/O操作。尽管我们通过多进程等方式来充分利用空闲的CPU资源,但是我们还是希望能够让CPU花费足够少的时间在I/O操作的调度上,这样就可以腾出更多的CPU时间来完成更多的I/O操作,事实上,如何让高速的CPU和慢速的I/O设备更好的协调工作,这是从现代计算机诞生到现在一直在探讨的话题,很多技术和策略都围绕它们展开。针对本章Web服务器并发处理能力的讨论范畴,我们所关注的I/O操作具体的说,主要是网络数据的接收和发送,以及磁盘文件的访问,我们将其归纳为多种模型,称为I/O模型,它们的最大本质区别便在于CPU的参与方式。PIO与与DMA在介绍I/O模型之前,有必要简单的说说慢速I/O设备和内存之间的数据传输方式。我们拿磁盘来说,很早很早以前,磁盘和内存之间的数据传输是需要CPU控制的,也就是说如果我们读取磁盘文件到内存中,数据要经过CPU存储转发,这种方式成为PIO。显然这种方式非常的不合理,需要占据大量的CPU时间来读取文件,造成文件访问时系统几乎停止响应。后来,DMA(直接内存访问,DirectMemoryAccess)取代了PIO,它可以不经过CPU而直接进行磁盘和内存的数据交换。在DMA模式下,CPU只须向DMA控制器下达指令,让DMA控制器来处理数据的传送,DMA控制器通过系统总线来传输数据,传送完毕再通知CPU,这样就很大程度上减轻了CPU占有率,大大节省系统资源,而它的传输速度与PIO的差异其实并不十分明显,因为这主要取决于慢速设备的速度。可以肯定的是,PIO模式的计算机我们已经很少见到。同步阻塞同步阻塞I/O说到阻塞,首先得说说I/O等待。造成等待的原因非常多,比如Web服务器在等待用户的访问,这便是等待,因为它不知道谁会来访问,所以只能等。随后,当某个用户通过浏览器发出请求,Web服务器与该浏览器建立TCP连接后,又要等待用户发出HTTP请求数据,用户的请求数据在网络上传输需要时间,进入服务器接收缓冲区队列以及被复制到进程地址空间,都需要时间。另外,假如浏览器和Web服务器采用HTTP长连接模式,那么在超时关闭连接之前,服务器还要等待浏览器发送其它的请求,这也是I/O等待。再比如,读取某磁盘文件的I/O操作,可能先要等待其它的磁盘访问操作,因为除非是RAID磁盘,否则磁头只有一个,只能一个个排队读取,即使是轮到自己了,在磁盘上读取数据本身也要花费时间。可见,I/O等待是不可避免的,那么既然有了等待,就会有阻塞,但是注意,我们说的阻塞是指当前发起I/O操作的进程被阻塞,并不是CPU被阻塞,事实上没有什么能让CPU阻塞的,CPU只知道拼命的计算,对于阻塞一无所知。另外,“同步”的概念在这里显得并不那么重要,只是为了和后边的异步I/O加以区分,我们在介绍异步事件通知和异步I/O的时候再来探讨同步和异步的区别。但是需要说明的是,对于磁盘文件的访问,也有一个所谓“同步”的选项,即使用O_SYNC标志打开文件。规范情况下,对磁盘文件调用read()将阻塞进程,一直到数据被拷贝到进程用户态内存空间,而对磁盘文件调用write()则不同,它会在数据被拷贝到内核缓冲区后便立即返回。如果使用O_SYNC标志打开文件,则对写文件操作产生影响,它使得write()必须等待数据真正写入磁盘后才返回。这样一来,同步阻塞I/O便是指,当进程调用某些涉及I/O操作的系统调用或库函数时,比如accept()、send()、recv()等,进程便暂停下来,等待I/O操作完成再继续运行。这是一种简单而有效的I/O模型,它可以和多进程结合起来有效的利用CPU资源,但是代价就是多进程的大量内存开销。比如在Apache-prefork模型中,某个子进程在等待请求时,进程阻塞在accept()调用,我们用strace跟踪如下:s-colin:~#strace-p17520Process17520attached-interrupttoquitaccept(3,可以看出,accept()在等待用户连接的到达,同时该进程停在此处成为阻塞状态。为了简单说明同步阻塞I/O以及和其它I/O模型的区别,我们举个有意思的例子。比如你去逛街,逛着逛着有点饿了,这时你看到商场里有小吃城,就去一个小吃店买一碗面条,交了钱,可面条做起来得需要时间,你也不知道什么时候可以做好,没办法,只好坐在那里等,等面条做好后吃完再继续去逛街。显然,这里的吃面条便是I/O操作,它要等待厨师做面条,还要等待自己把面条吃完。同步非阻塞同步非阻塞I/O在同步阻塞I/O中,进程实际上等待的时间可能包括两部分,一个是等待数据的就绪,另一个是等待数据的复制,对于网络I/O来说,前者的时间可能要更长一些。与此不同的是,同步非阻塞I/O的调用不会等待数据的就绪,如果数据不可读或者不可写,它会立即返回告诉进程。比如我们使用非阻塞recv()接收网络数据的时候,如果网卡缓冲区中没有可接收的数据,函数就及时返回,告诉进程没有数据可读了。相比于阻塞I/O,这种非阻塞I/O结合反复的轮询来尝试数据是否就绪,防止进程被阻塞,最大的好处便在于可以在一个进程里同时处理多个I/O操作。但正是由于需要进程执行多次的轮询来查看数据是否就绪,这花费了大量的CPU时间,使得进程处于忙碌等待状态。回到买面条的故事中,假如你不甘心坐着等面条做好,想去顺便逛逛街,可又担心面条做好后没及时去领取,所以你逛一会便跑去看看面条是否做好,往返了很多次,最后虽然及时的吃上了面条,但是却累得气喘吁吁。非阻塞I/O一般只针对网络I/O有效,我们只要在socket的选项设置中使用O_NONBLOCK即可,这样对于该socket的send()或recv()便采用非阻塞方式。值得注意的是,对于磁盘I/O,非阻塞I/O并不产生效果。多路多路I/O就绪通知就绪通知在实际应用中,特别是Web服务器,同时处理大量的文件描述符是必不可少的,但是使用同步非阻塞I/O显然不是最佳的选择,在这种模型下,我们知道如果服务器想要同时接收多个TCP连接的数据,就必须轮流对每个socket调用接收数据的方法,比如recv()。不管这些socket有没有可以接收的数据,都要询问一遍,假如大部分socket并没有数据可以接收,那么进程便会浪费很多CPU时间用于检查这些socket,这显然不是我们所希望看到的。多路I/O就绪通知的出现,提供了对大量文件描述符就绪检查的高性能方案,它允许进程通过一种方法来同时监视所有文件描述符,并可以快速获得所有就绪的文件描述符,然后只针对这些文件描述符进行数据访问。回到买面条的故事中,假如你不止买了一份面条,还在其它几个小吃店买了饺子、粥、馅饼等,因为一起逛街的朋友看到你的面条后也饿了。这些东西都需要时间来等待制作。在同步非阻塞I/O模型中,你要轮流不停的去各个小吃店询问进度,痛苦不堪。现在引入多路I/O就绪通知后,小吃城管理处给大厅安装了一块电子屏幕,以后所有小吃店的食物做好后,都会显示在屏幕上,这可真是个好消息,你只需要间隔性的看看大屏幕就可以了,也许你还可以同时逛逛附近的商店,在不远处也可以看到大屏幕。需要注意的是,I/O就绪通知只是帮助我们快速获得就绪的文件描述符,当得知数据就绪后,就访问数据本身而言,仍然需要选择阻塞或非阻塞的访问方式,一般我们选择非阻塞方式,以防止任何意外的等待阻塞整个进程,比如有时就绪通知只代表一个内核的提示,也许此时文件描述符尚未真正准备好或者已经被客户端关闭连接。由于平台和历史等原因,多路I/O就绪通知有很多不同的实现,在检查大量文件描述符时的性能也存在一定的差异。说到这里,我们有必要简单介绍一下Unix家族的历史,这有助于我们更加深刻的了解这些依赖于平台的技术。记住,最早的Unix诞生于贝尔实验室,在随后的十年,Unix在学术机构和大型企业中被广泛应用。贝尔实验室是AT&T收购了西方电子公司的研究部门后设立的一个独立实体,AT&T这时以廉价的许可将Unix的源代码授权给一些研究机构和大学,供他们研究和教学使用,这使得很多机构在此基础上对Unix进行了完善和扩展,促进了Unix的发展,同时产生了一些新的变种版本,其中最著名的变种之一便是由加州大学Berkeley分校开发的BSD,这一变种对Unix有着重大贡献和深远影响。由于BSD开始被很多企业广泛采用,不久之后,AT&T意识到了Unix的商业价值,开始停止源代码授权,同时为了统一混乱的Unix版本,AT&T综合了其它大学和企业开发的各种Unix,开发了UNIXSystemVRelease1。这时候BSD已经非常成熟,比如select就诞生于这时候的4.2BSD,而TCP/IP则诞生于4.1BSD,它的代码也成为以后几乎所有操作系统TCP/IP实现代码的前辈,包括Windows。BSD不断增大的影响力终于引起了AT&T的关注,AT&T将BSD告上了法庭,一场源代码版权官司开始了,一直持续到AT&T将Unix实验项目卖给Novell。Novell采取了开明的态度,它允许BSD自由开发,但是前提是必须删除来自AT&T的代码,就这样,BSD诞生了全新的版本4.4BSDLite,这个版本不存在法律问题,所以它成为很多现代自由版Unix的基础,它们和Unix以及Linux一起,共同成为Unix大家族的成员。BSD在发展中也逐渐衍生出3个主要的分支:FreeBSD、OpenBSD和NetBSD。此后的几十年中,Unix仍在不断变化,其版权所有者不断变更,授权者的数量也在增加。有很多大公司在取得了Unix的授权之后,开发了自己的Unix产品,比如IBM的AIX,HP的HPUX,Sun的Solaris、SGI的IRIX,以及Apple的MacOS。正是在Unix之间的利益纷争以及其商业化运作背景下,乱世出英雄,Linux诞生了,它天生便吸取了Unix的教训,在版权问题上直接采用自由开发的GPL开源许可,这种开发精神使得Linux就像当年的BSD一样,发展迅速,同时又由于GPL的使用,很好的规避了变种并存的情况。这真是一部充满斗争的历史,它深深影响到几十年后的我们,我们这一章介绍的技术基本都来自于那个时代,所以当你被很多相似的实现方法搞的眼花缭乱时,想想那
本文标题:服务器并发处理能力
链接地址:https://www.777doc.com/doc-1594475 .html