[转] 关于对几种服务器模型的自我理解
说实话我没看懂..
---------------------
转自:http://terry831010.blog.163.com/blog/static/6916117120140683024706/
最近部署服务器比较频繁,而基本都是麻木的选择了nginx+php-fpm的架构,并且网络上基本呈现一边倒的趋势,认为nginx+php-fpm是
高效能的解决方案,原因是:nginx使用单进程+单线程+非阻塞+基于事件的IO。而且我也一直深信不疑。但是,对于这种服务器模型的底层原理又是什
么?为什么高效?果真如此吗?而且最近听了一个大牛的讲课后,他竟然说“apache+php
module的速度是nginx+php-fpm的3-4倍” ? 更让我非常迫切的想了解几套服务器模型各自的原理和优缺点。
在查了近乎一周的资料,关于服务器模型总结如下:
一、多进程单线程+堵塞+同步IO。 每个IO都是堵塞的,上一个IO未处理完或未就绪,整个进程会被阻塞在次,因此要想并发的处理多个请求,就必须为每个请求fork一个进程。由于是堵塞IO,每个进程同时只能处理一个请求。这种是最简单的模型。
二、多进程多线程+堵塞+同步IO 。和第一种的区别在于每个进程又可以new多个线程来处理每个请求。
三、多进程单线程+多路复用(select)+同步IO。apache的prefork是这种的代表。预先fork几个进程,但是每个进程可以使用多路复用的IO,同时处理多个请求。
四、多进程多线程+多路复用(select)+同步IO。apache的worker模式是这种的代表。预先fork几个进程,每个进程又可以为每个请求new一个线程。
五、多进程单线程+多路复用(epool)+基于事件异步IO。nginx的模型。 预先fork几个进程,采用epool的基于事件的多路复用IO模型,每个进程可以同时处理多个请求。
六、单进程单线程+非堵塞+异步IO。node.js宣称自己的服务器是这种模型。只有一个进程、一个线程,整个请求都是非堵塞的,且每个IO都是异步的。
.......
好了,以上是常见的并发服务器模型。上面提到的堵塞、非堵塞、多路复用、异步、同步、基于事件的几个概念也有必要澄清,否则还是无法说出本质。
在说堵塞/非堵塞,同步/异步之前,必且必须提到一个IO涉及到的两个对象和两个阶段。
我们一个简单的read、write,一个socket来了,accept、readfrom都是一个IO操作。
那么,这个IO是会涉及到那些对象?
通常read、write都是我们的应用发起的,难道你在程序里写个read,是你的程序就直接到硬盘或者网卡里读数据了吗?
当然不是,如果这样,还要操作系统干嘛。
应用(你写的服务器-比如:nginx)只负责发起IO调用,真正实现数据准备,读硬盘的还是操作系统(内核)。所以:一次IO调用涉及应用、内核两个对
象。
一发起调用,应用就可以直接读数据了吗?你以为直接读内存呀?
内核很忙,所有的东西被是他接管,应用发起一个调用后,他首先需要将需要读写的内容写到自己的缓冲区,直到该资源一切就绪,才通知应用去自己的缓冲区读或
写数据到缓冲区。所以:一次IO分两个阶段,数据的准备阶段(内核判断资源是否就绪)、数据的操作阶段(具体的读写)。比如到银行排队(数据准备阶段),
办理业务(数据操作阶段)。任何脱离IO调用的阶段讲堵塞、同步的,都有点让人不明觉厉。
了解了IO的两个阶段后,堵塞/非堵塞/IO多路复用、同步/异步就要出场了。
还是以到银行办理业务为例(不知道为什么都是这个例子,太有代表性了)。排队---数据准备阶段,有三种方式。一是:大家都排队,前一个人没办理完,后面
的人别想到处跑,做自己的事情。这就是堵塞方式。第二种是:每个人到窗口(内核)拿个号并登记,然后坐等。这样,大家就基本自由了,可以各行其事了,但
是,客服(应用)就忙了,他要不断去问窗口(不要把应用看成人,他没眼睛的,只能轮询),有人来了吗?有人来了吗?CPU大量被占用。第三种:买一个报号
器(select或者epool)。每个人拿号,都会被窗口(内核)交给报号器,然后客服只需要问报号器,由于报号器非堵塞的一次可以接受多个人拿号,所
以,此种模式很轻松的解决了堵塞和非堵塞的问题,这就是多路复用IO。
到这里,基本清楚了,平常所说的非堵塞式、同步IO。其实是说的一次IO处理中两个阶段各自的处理机制。
好了,接上多路复用IO的讨论。排队的问题解决了,开始业务办理的阶段了。这里,这个处理阶段又有同步和异步两种。 同步IO:指的是应用和内核的同步。内核读到需要的数据到自己的缓冲区,应用从内核缓冲区复制数据到自己的内存空间,此段时间内应用和内核是同步的,应用整个被堵住(之所以不用堵塞这个次,担心和上面的堵塞搞混,其实意思是一样的,都是应用不能处理下面的逻辑了)。
客服和客户之间隔着一个窗口(内核),办理业务是需要资料(数据)的,比如身份证,客户将资料提交到窗口(内核),客服(应用)从窗口(内核)拿数据开始
办理。假如有个人没有身份证,那么他就要去旁边复印,这时,整个处理队列都会被他堵住,等他复印完。这就是同步,CPU被浪费,虽然很清闲,但是确实在做
无谓的等待。 如果客服继续办理第二个人的业务,当第一个人复印好了,通过窗口通知并提交给他,这就是异步。应用不需要等待数据就绪,而是通过内核通知他,并且数据准备好后,内核或者额外的线程将内核缓冲区的数据复制到程序的数据区,这就是异步的本质。
综上可见,采用多路复用IO或者非堵塞IO似乎很好的解决了排队的问题,提高了系统的吞吐量,但是整体处理业务的时间并没有得到提升。想想第一个人办理需
要10分钟,第二个人办理还是需要等10分钟.......
排队的优化只能解决一次可以同时接受多个人的请求问题,请求的处理时间并没有被缩短。真正在数据操作阶段(业务办理阶段)采用异步IO才能根本解决问题。
我们看看nginx和apache,这里没必要探讨select和epool的性能问题,因为我们知道epool和select只解决了第一阶段数据准备
阶段的问题,真正处理业务的部分(php或者静态文件)的方式才是整个性能的根本。先说发布静态文件,apache采用的是同步IO,nginx采用的是
基于事件的异步IO,所以从这里看nginx是胜利了。
那么再看php,我们看下apache和php的结合,php是在apache启动的时候被加载到自己的内存空间并常驻在内存的。每个请求过
来,apache会fork一个进程或new一个线程处理,php内核会被复制一份独立暂一份内存空间和分出cpu时间片去处理。再看nginx,他是以
socket与php-fpm通信的,也就是说nginx虽然前面的业务排队问题虽然解决的很好,但是业务处理时间还要看php-fpm的脸色。php-
fpm呢?多进程单线程阻塞同步IO,这里基本涉及到的是具体业务逻辑的计算,所以这个时间才真是实实在在的程序执行时间。这种模型,和apache的多
进程或者多线程基本一致。但是,多了什么? 多了nginx与php-fpm的通信(网络IO),这个时间能忽略吗? 恐怕不能,任何IO都是耗时的。
因此,从原理上看,nginx+php-fpm不见得要快于apache,而越来越多的人选择nginx,我想主要有两点:一:nginx发布静态文件性
能优秀(取每个文件的IO都是nginx自己完成的,且是异步IO)。二:架构上的优势。nginx与php可以分布式部署。各种负载均衡的架构应运而
生。 前面是nginx做总入口,通过反向代理分流到后端多台php-fpm或者apache上。
完。肯定有侃的不清楚的地方,但愿别误导您。如误导,请回归正途。