Nginx学习笔记(三)—Nginx的I/O模型详解

☆樱花仙子☆ 提交于 2020-01-29 03:37:47

一、web请求处理机制

1.1 多进程方式

多进程方式: 服务器每接受到一个客户端请求就有服务器的 主进程生成一个子进程响应客户端,直到用户关闭连接
优点:处理速度快,子进程之间相互独立
缺点:访问过多会导致服务器资源耗尽而无法再提供请求

1.2 多线程方式

多线程方式: :与多进程方式类似,但是每收到一个客户端请求会有 服务进程派生出一个线程来和客户方进行交互。
优点:一个线程的开销远远小于一个进程,因此减轻了web服务器对系统资源的要求
缺点:当多个线程位于同一个进程内工作的时候,可以相互访问同一的内存地址空间,所以
线程相互影响
一旦主进程挂掉则所有子线程都不能工作,IIS服务器使用了多线程的方式,需要间隔一段时间就重启一次才能稳定。

1.3 异步

异步的方式:nginx的epoll,apache的event等

二、同步异步,阻塞非阻塞和Nginx的I/O模型

2.1 同步与异步(应用程序与内核的交互方式)

  • 同步: 进程发出数据后,等内核返回响应以后才继续下一个请求,即如果内核一直不返回数据,那么进程就一直等,直到天荒地老,死机error。
  • 异步: 进程发出数据后,不等内核返回响应,接着处理下一个请求,内核通过回调函数来处理进程。Nginx是异步的。
  • 同步和异步关注的是消息通信机制,在同步机制中,所有的请求在服务器端得到同步,发送方和接收方对请求的处理步调是一致的;在异步机制中,所有来自发送方的请求形成一个队列,接收方处理完成后通知发送方。
  • 举个通俗的例子: 你打电话问书店老板有没有《一千零一夜》这本书,如果是同步通信机制,书店老板会说,你稍等,“我查一下”,然后开始查啊查,等查好了(可能是5秒,也可能是一天)告诉你结果(返回结果)。而异步通信机制,书店老板直接告诉你我查一下啊,查好了打电话给你,然后直接挂电话了(不返回结果)。然后查好了,他会主动打电话给你。在这里老板通过"回电"这种方式来回调。

2.2 阻塞与非阻塞(内核与IO设备的交互方式)

可以理解为内核与IO设备的交互方式,当内核收到进程请求IO数据时候的处理方式,也可以简单理解为内核需要做一件事能不能立即得到返回应答,如果不能立即获得返回,需要等待,那就阻塞了,否则就可以理解为非阻塞。

  • 阻塞:IO调用不能立即返回结果,即一个进程发起的IO请求不能得到立即满足时,进程就要一直等到内核响应,内核要把数据从IO设备复制到内核空间,再返回给进程,这是阻塞。
  • 非阻塞:IO调用可以立即返回结果,一个进程发起的IO进程不能立即满足时,不在等待,而是一遍一遍的轮询查看IO是否完成且立刻执行下一个调用。

2.3 Nginx支持的并发模型

nginx 支持多种并发模型,并发模型的具体实现根据系统平台而有所不同。
在支持多种并发模型的平台上,nginx 自动选择最高效的模型。但也可使用 use 指令在配置文件中显式地定义某个并发模型。

  • select: IO多路复用、标准并发模型。在编译 nginx 时,如果所使用的系统平台没有更高效的并发模型,select 模块将被自动编译。configure 脚本的选项:–with-select_module 和 --without-select_module 可被用来强制性地开启或禁止 select 模块的编译。
  • poll: IO多路复用、标准并发模型。与 select 类似,在编译 nginx 时,如果所使用的系统平台没有更高效的并发模型,poll 模块将被自动编译。configure 脚本的选项:–with-poll_module 和 --without-poll_module 可用于强制性地开启或禁止 poll 模块的编译
  • epoll: IO多路复用、高效并发模型,可在 Linux 2.6+ 及以上内核可以使用
  • kqueue: IO多路复用、高效并发模型,可在 FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0, and Mac OS X 平台中使用
  • /dev/poll: 高效并发模型,可在 Solaris 7 11/99+, HP/UX 11.22+ (eventport), IRIX 6.5.15+, and Tru64 UNIX 5.1A+ 平台使用
  • eventport: 高效并发模型,可用于 Solaris 10 平台,PS:由于一些已知的问题,建议 使用/dev/poll替代。

1、为什么epoll快?

比较一下Apache常用的select,和Nginx常用的epoll

select:

最大并发数限制,因为一个进程所打开的 FD (文件描述符)是有限制的,由 FD_SETSIZE 设置,默认值为1024/2048,因此Select 模型的最大并发数就被相应限制了。

效率问题,select 每次调用都会线性扫描全部的 FD 集合,这样效率就会呈现线性下降,把 FD_SETSIZE改大的后果就是,大家都慢慢来,什么?都超时了。

内核 / 用户空间 内存拷贝问题,如何让内核把 FD 消息通知给用户空间呢?在这个问题上 select 采取了内存拷贝方法,在FD非常多的时候,非常的耗费时间。

总结为:1、连接数受限 2、查找配对速度慢 3、数据由内核拷贝到用户态消耗时间

epoll:

Epoll 没有最大并发连接的限制,上限是最大可以打开文件的数目,这个数字一般远大于 2048, 一般来说这个数目和系统内存关系很大,具体数目可以 cat /proc/sys/fs/file-max 查看。

效率提升, Epoll 最大的优点就在于它只管你“活跃”的连接 ,而跟连接总数无关,因此在实际的网络环境中, Epoll 的效率就会远远高于 select 和 poll 。

内存共享, Epoll 在这点上使用了“共享内存 ”,这个内存拷贝也省略了。

举个例子:
假设你在大学中读书,要等待一个朋友来访,而这个朋友只知道你在A号楼,但是不知道你具体住在哪里,于是你们约好了在A号楼门口见面.
使用的阻塞IO模型来处理这个问题,那么你就只能一直守候在A号楼门口等待朋友的到来,在这段时间里你不能做别的事情,不难知道,这种方式的效率是低下的.
使用多路复用IO模型来处理这个问题.你告诉你的朋友来了A号楼找楼管大妈,让她告诉你该怎么走.这里的楼管大妈扮演的就是多路复用IO的角色.

  • select版大妈做的是如下的事情:比如同学甲的朋友来了,select版大妈比较笨,她带着朋友挨个房间进行查询谁是同学甲,你等的朋友来了,于是在实际的代码中,select版大妈做的是以下的事情:

     int n = select(&readset,NULL,NULL,100);  
      for (int i= 0; n > 0; ++i) {
      if (FD_ISSET(fdarray[i], &readset))
      {
          do_something(fdarray[i]);
          --n;
      } } ```
    
  • epoll版大妈就比较先进了,她记下了同学甲的信息,比如说他的房间号,那么等同学甲的朋友到来时,只需要告诉该朋友同学甲在哪个房间即可,不用自己亲自带着人满大楼的找人了.于是epoll版大妈做的事情可以用如下的代码表示:

     n=epoll_wait(epfd,events,20,500);  
      for(i=0;i<n;++i)
       {
        do_something(events[n]);
         } ```
    
    

Nginx的IO模型是 基于事件驱动 的,使得应用程序在多个IO句柄间快速切换,实现所谓的异步IO。事件驱动服务器,最适合做的就是IO密集型工作,如反向代理,它在客户端与WEB服务器之间起一个数据中转作用,纯粹是IO操作,自身并不涉及到复杂计算。反向代理用事件驱动来做,显然更好,一个工作进程就可以run了,没有进程、线程管理的开销,CPU、内存消耗都小。

Apache这类应用服务器,一般要跑具体的业务应用,如科学计算、图形图像等。它们很可能是CPU密集型的服务,事件驱动并不合适。 例如一个计算耗时2秒,那么这2秒就是完全阻塞的,什么event都没用。想想MySQL如果改成事件驱动会怎么样,一个大型的join或sort就会阻塞住所有客户端。这个时候多进程或线程就体现出优势,每个进程各干各的事,互不阻塞和干扰。当然,现代CPU越来越快,单个计算阻塞的时间可能很小,但只要有阻塞,事件编程就毫无优势。所以进程、线程这类技术,并不会消失,而是与事件机制相辅相成,长期存在。

总的说来,事件驱动适合于IO密集型服务,多进程或线程适合于CPU密集型服务,其实也就是说nginx比较适合做前端代理,或者处理静态文件(尤其高并发情况下),而apache适合做后端的应用服务器,功能强大[php,rewrite…],稳定性高。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!