https://www.tuicool.com/articles/AzmiY3
关于select与epoll
两种IO模型,都属于多路IO就绪通知,提供了对大量文件描述符就绪检查的高性能方案,只不过实现方式有所不同:
select:
一个select()系统调用来监视包含多个文件描述符的数组,当select返回,该数组中就绪的文件描述符便会被内核修改标志位。
select的 跨平台 做的很好,几乎每个平台都支持。
select缺点有以下三点:
- 单个进程能够 监视的文件描述符的数量存在最大限制
- select()所维护的 存储大量文件描述符的数据结构 ,随着文件描述符数量的增长,其在用户态和内核的地址空间的复制所引发的开销也会线性增长
- 由于网络响应时间的延迟使得大量TCP连接处于非活跃状态,但调用select()还是会对 所有的socket进行一次线性扫描 ,会造成一定的开销
poll:
poll是unix沿用select自己重新实现了一遍,唯一解决的问题是poll 没有最大文件描述符数量的限制
epoll:
epoll带来了两个优势,大幅度提升了性能:
- 基于事件的就绪通知方式 ,select/poll方式,进程只有在调用一定的方法后,内核才会对所有监视的文件描述符进行扫描,而epoll事件通过epoll_ctl()注册一个文件描述符,一旦某个文件描述符就绪时,内核会采用类似call back的回调机制,迅速激活这个文件描述符,epoll_wait()便会得到通知
- 调用一次epoll_wait()获得就绪文件描述符时,返回的并不是实际的描述符,而是一个代表就绪描述符数量的值,拿到这些值去epoll指定的一个数组中依次取得相应数量的文件描述符即可,这里使用内存映射(mmap)技术, 避免了复制大量文件描述符带来的开销
当然epoll也有一定的局限性, epoll只有Linux2.6才有实现 ,而其他平台都没有,这和apache这种优秀的跨平台服务器,显然是有些背道而驰了。
Apache与Nginx:
Apache与Nginx的性能谁更高效,取决于其服务器的并发策略以及其面对的场景:
并发策略:
我们目前使用的 Apache是基于一个线程处理一个请求的非阻塞IO并发策略 。这种方式允许一个进程中通过多个线程来处理多个连接,其中每个线程处理一个连接。Apache使用其worker模块实现这种方式,目的是减少perfork模式中太多进程的开销,使得apache可以支持更多的并发连接。
至于,非阻塞IO的实现,是通过一个子进程负责accept(),一旦接收到连接后,便将任务分配给适当worker的线程。
由于apache的线程使用的是内核进程调度器管理的轻量级进程,因此与perfork模式比较,进程上下文切换的开销依然存在,性能提升不是很明显。
而 Nginx使用的是一个进程处理多个连接、非阻塞IO模式 ,这种模式最特别的是设计了独立的listener进程,专门负责接收新的连接,再分配给各个worker,当然为了减少任务调度的开销,一般都是由worker进程来进行接收。
而IO模型层面,Nginx选择epoll,此方式高效主要在于其基于事件的就绪通知机制,在高连接数的场景下,epoll通知方式更具优势。另外,epoll方式只关注活跃连接,而不像select方式需要扫描所有的文件描述符,这样在大量连接的场景下,epoll方式优势会更加明显。
面对的场景:
我们反观一下网站目前的状况和场景:
|
上图可以看到,rt监控当中,由于我们是动态网站,网站的大部分内容都需要动态获取、计算、输出,因此响应时间普遍位于100毫秒以上,响应时间较长,服务器必将创建更多的连接处理这些请求,而tcp监控当中,可以看到由于TCP协议特有的特性,服务端主动关闭一个连接,连接会进入等待超时的状态,且此状态会持续2MSL(即两倍的数据包最大生存时间,这个时 间长短跟操作系统有关,一般会在1-4分钟),因此服务器端会保留一定量time_wait连接,管理大量的连接也会对服务器造成一定的成本,而epoll在多连接并发处理以及管理这两方面,都较于select具有很大的优势。这也正是高并发、高连接的互联网网站大量使用Nginx服务器的原因所在。