Nginx的分布式系统归功于其选择的epoll实体模型,这种模型不同于传统的服务器程序架构。epoll出现在linux内核2.6之后。根据下面的Apache和Nginx原理。
传统的apache都是在多进程或者线程同步工作,假设是在多进程工作(prefork)。Apache会形成很多进程,类似于进程池的原理,但是这里的进程池会随着请求的增加而升级。对于每个连接,apache在一个进程中关闭所有交通事故。实际上是recv(),它根据URI执行硬盘I/O来查找文档,但是send()也被全部阻塞。其实简而言之就是apche对sockets的所有I/O,读或者写,但是所有的读或者写都是阻塞的。阻塞意味着进程要离线休眠,所以一旦有很多连接,apache就必须形成大量的进程来响应请求。一旦进程多了,CPU就会频繁切换进程,消耗大量的资源和时间。所以降低了阿帕奇的特性。总之解决不了那么多流程。其实仔细想想,如果流程的每一个请求都不被阻塞,效率无疑会提高很多。
Nginx使用epoll实体模型,多线程是非阻塞的。就Nginx而言,一个详细的连接请求解析被分成多个事件,一个接一个。比如accept(),recv(),硬盘I/O,send()等。,每个部分由一个相对的控制模块来解决,一个详细的请求可能由几十个控制模块来解决。真正的关键是事件收集和分发控制模块,它是管理方法的所有控制模块的关键。只有关键控制模块的生产调度才能使匹配的控制模块占用CPU资源,进而解决请求。以一个HTTP请求为例。首先,在事件收集和分发控制模块中申请注册感兴趣的监控事件。申请注册后,马上回去,不堵。然后,就不用担心了。有连接的时候内核会通知你(epoll的轮询会通知进程),cpu可以解决其他的事情。一旦有请求,所有请求的相对上下文都会被赋值(其实已经提前赋值了),新的有趣事件(read函数)会在这一刻注册。手机客户端的数据信息一来,核心就会自动通知进程可以读取数据。看完数据信息,会进行分析。分析完后会去硬盘找电影(I/O)。I/O一旦完成,进程就会通知进程,进程就开始把数据信息发回手机客户端。都是把一个请求分成很多个环节,每个环节去很多个控制模块申请注册,然后就解决了,都是多线程无阻塞的。多线程在这里的意思是做一件事,不用等待结果。如果你做得好,你会得到充分的信息。
选择/epoll功能
select的特点:当select选择一个句柄时,它解析xml的所有句柄。换句话说,当一个句柄有事件响应时,select必须先解析xml的所有句柄,才能获得任何有事件公告的句柄,所以效率极低。但如果连接很少,select和epoll的LT打开方式在特性上没有太大区别。
在这里,我想再说一句。适用于select的句柄数量有限制,只有1024个句柄适用。这受到手柄组合的限制。如果超过这个限度,很可能造成溢出,发现问题非常困难。当然这个主要参数可以根据更换linux的socket内核来调整。
epoll的特点:句柄事件的选择
epoll不是解析xml,而是响应事件,即不需要解析所有xml句柄的链表,直接选择句柄上的事件,所以效率很高。核心将句柄存储在红色和黑色的树中。
对于epoll,ET和LT也有区别,LT表示电平开,ET表示边沿开。两者在特性和编码完成度上的差别也是非常大的。
Epoll实体模型的关键是正确处理多个高并发客户端的请求,以及网络服务器和手机客户端之间的数据信息交互。实际完成过程如下:
(a)使用epoll_create()函数创建文件描述,设置大量会管理方法的socket描述符。
(b)建立与epoll关系的接受流程。应用软件可以建立几个受理流程来解决epoll上的readannouncement事件,流程总数取决于程序流程的实际必要性。
(c)建立监听套接字描述符ListenSock;将描述符设置为非阻塞模式,启用Listen()函数监听socket上的新连接请求,在epoll_event结构中设置要解决的事件类型EPOLLIN,工作模式为epoll_ET,提高工作效率。另外,应用epoll_ctl()注册事件,最后启动互联网监控进程。
(d)互联网监控进程启动循环系统,epoll_wait()等待epoll事件。
(e)如果epoll事件指示有新的连接请求,启用accept()函数,将客户端套接字描述符添加到epoll_dataunion中,并将描述符设置为非阻塞,并将epoll_event结构中要解决的事件类型设置为read和write。工作模式为epoll_ET。
(f)如果epoll事件指示套接字描述符上有可读的数据信息,则将可读序列添加到套接字描述符中,通知接收进程读取数据信息,并将接收到的数据信息放入读取数据的链表中。逻辑求解后,将有反馈的数据文件放入传输数据的链表中,等待推送进程推送。
epoll的实际 *** 作非常简单,只有四个API:epoll_create、epoll_CTL、epoll_wait和close。
举一个简单的例子来展示Apache的工作。人们通常去餐馆吃饭。酒店的工作模式是一个服务员全程服务顾客。步骤如下:服务员在门口等顾客(听),顾客到达分配给他们的餐桌(接受),等顾客点菜(请求uri),去餐厅厨房让厨师提交订单做饭(硬盘I/O),等餐厅厨房做好(读),然后服务顾客(发送),而且服务员都出来了(客户那么多的时候流程(HTTP请求那么多),酒店只能按照调用大量服务员来服务项目(fork流程)。但由于酒店的资源相对有限(CPU),一旦服务员太多,管理成本很高(CPU上下文转换),就会导致短板。
如何再处理Nginx?在酒店门口挂一个电子门铃(听申请注册epoll样机)。一旦有客户(HTTP请求)到了,就派服务员去接受。以后服务员就要忙别的事情了(比如再招呼客人)。客户点餐的时候会叫服务员(数据信息来读())。服务员会回来把收据拿到餐厅厨房(硬盘I/O)。服务员又来做别的事情了。当餐厅厨房完成菜品的时候,他们也会叫服务员(硬盘I/O完成),然后服务员把菜端给顾客(send())。当餐厅厨房完成一道菜时,它会给顾客一道。中间服务员可以做好其他事情。整个过程分为许多环节,每个环节都有一个相对的服务项目控制模块。想想看,这样一旦顾客多了,酒店也能招待一大批人。
无论是Nginx还是Squid等反向代理,其网络模式都是事件驱动的。事件其实是很老的技术,最初的选择和投票都是这样的。之后根据核心公告出现了更高级的事件系统,比如libevent中的epoll,改进了事件驱动的特性。事件驱动的本质还是IO事件。应用软件在几个IO句柄之间快速转换,用通俗易懂的英语完成异步IO。事件驱动的网络服务器最适合这种IO密集型的工作,比如反向代理,在手机客户端和WEB集群服务器之间起到数据信息传递的作用。纯碎片化是IO的实际 *** 作,不涉及复杂的计算。反向代理是事件驱动的,显然更强。一个工作进程可以运行,没有进程和进程管理方法的开销,CPU和运行内存消耗小。
所以,Nginx和Squid都是这么做的。自然,Nginx也可以是多进程事件驱动的方式。很多进程运行libevent,没有Apache的进程有一百多个。Nginx解决静态数据文件的实际效果也很好,因为静态数据文件本身也是硬盘IO的实际 *** 作,处理方法也是一样的。这对于成千上万的高并发连接来说毫无价值。写一个互联网程序流,几万块钱就能解决高并发,但如果大部分手机客户端都被屏蔽在那里,那就没有利用价值了。
我们再来看看Apache或者Resin网站服务器,之所以经常被称为网站服务器,是因为它们确实要运行实际的业务流程,比如计算机应用、图像处理、数据库查询读写能力等。它们很可能是CPU密集型的服务项目,事件驱动不适合。例如,如果一个计算需要2秒钟,那么这2秒钟将被完全阻塞,所有事件都不起作用。想想如果MySQL改成事件驱动,一个大中型的join或者sort就把所有移动客户端都屏蔽了,会怎么样?这时候很多流程或过程体现出自己的优势,各个流程各做各的,互相阻碍,互相影响。自然,当代的CPU越来越快,光是衡量拥塞的时间很可能就不长了。但是,如果出现拥塞,事件编程就没有优势了。所以,流程和过程的技术是不会消亡的,但它与事件系统密切相关,并且长期存在。
总之,事件驱动适合IO密集型服务项目,多进程或多进程适合CPU密集型服务项目。都有各自的优势,不存在谁取代谁的倾向。
另:Apache和Nginx运作基本原理分析为什么Nginx比ApacheHttpd更高效:原文
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)