apache与Nginx的主要区别(内核多路I/O之select与epoll的区别)

      高并发连接的情况下,Nginx是Apache服务器不错的替代品。Nginx同时也可以作为7层负载均衡服务器来使用。根据一些人的测试,Nginx + PHP(FastCGI) 可以承受3万以上的并发连接数,相当于同等环境下Apache的10倍

   为什么Nginx的性能要比Apache高得多?这得益于Nginx使用了最新的epoll(Linux 2.6内核)和kqueue(freebsd)网络I/O模型,而Apache则使用的是传统的select模型。目前Linux下能够承受高并发访问的Squid、Memcached都采用的是epoll网络I/O模型。

   下面解释一下什么叫epoll模型。

    epoll是Linux内核为处理大批量句柄而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著减少程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。因为它会复用文件描述符集合来传递结果而不用迫使开发者每次等待事件之前都必须重新准备要被侦听的文件描述符集合,另一点原因就是获取事件的时候,它无须遍历整个被侦听的描述符集,只要遍历那些被内核I/O事件异步唤醒而加入Ready队列的描述符集合就行了。

通过在包含一个头文件#include <sys/epoll.h>以及几个简单的API将可以大大的提高你的网络服务器的支持人数。

epoll的操作总共过4个API:epoll_create, epoll_ctl, epoll_wait和close。epoll的接口如下:

int epoll_create(int size);
int epoll_ctl(int epfd,int op, int fd, struct epoll_event *event);
int epoll_wait(int epfd,struct epoll_event * events, int max_events, int timeout);


其中用到的两个关于epoll的数据类型:

typedef union epoll_data {
                void *ptr;
                int fd;
                __uint32_t u32;
                __uint64_t u64;
            } epoll_data_t;
struct epoll_event {
         __uint32_t events;      /* Epoll events */
         epoll_data_t data;      /* User data variable */
};

epoll_create函数创建epoll文件描述符,参数size并不是限制了epoll所能监听的描述符最大个数,只是对内核初始分配内部数据结构的一个建议。返回是epoll描述符。-1表示创建失败。这里需要注意的是,epoll创建好了之后,会占用一个fd的值。

epoll_ctl 控制对指定描述符fd执行op操作。

op操作有三种:

EPOLL_CTL_ADD:添加新的描述符fd;

EPOLL_CTL_DEL:删除文件描述符fd;

EPOLL_CTL_MOD:修改文件描述符fd的监听事件。

这个函数中的epfd是epoll_create的返回值。

event是与fd关联的监听事件,这些事件主要有以下几种:

EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT:表示对应的文件描述符可以写;
EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR:表示对应的文件描述符发生错误;
EPOLLHUP:表示对应的文件描述符被挂断;
EPOLLET: 将EPOLL设为边缘触发(Edge Triggered)模式,这是相对于水平触发(Level Triggered)来说的;
EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里。

epoll_wait 等待epfd上的io事件,最多返回maxevents个事件。

在select/poll中,进程只有在调用一定的方法后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。

epoll的优点主要是一下几个方面:

1. 监视的描述符数量不受限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat/proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。select的最大缺点就是进程打开的fd是有数量限制的。这对于连接数量比较大的服务器来说根本不能满足。虽然也可以选择多进程的解决方案( Apache就是这样实现的),不过虽然linux上面创建进程的代价比较小,但仍旧是不可忽视的,加上进程间数据同步远比不上线程间同步的高效,所以也不是一种完美的方案。

2. IO的效率不会随着监视fd的数量的增长而下降。epoll不同于select和poll轮询的方式,而是通过每个fd定义的回调函数来实现的。只有就绪的fd才会执行回调函数。

3.支持电平触发边沿触发(只告诉进程哪些文件描述符刚刚变为就绪状态,它只说一遍,如果我们没有采取行动,那么它将不会再次告知,这种方式称为边缘触发)两种方式,理论上边缘触发的性能要更高一些,但是代码实现相当复杂。

4.mmap加速内核与用户空间的信息传递。epoll是通过内核于用户空间mmap同一块内存,避免了不必要的内存拷贝。

有关epoll的具体使用参考:点击打开链接

关于几种多路I/O的比较:点击打开链接

打个比方的话,就是这样的:

假设你在大学读书,住的宿舍楼有很多间房间,你的朋友要来找你。select版宿管大妈就会带着你的朋友挨个房间去找,直到找到你为止。而epoll版宿管大妈会先记下每位同学的房间号,你的朋友来时,只需告诉你的朋友你住在哪个房间即可,不用亲自带着你的朋友满大楼找人。如果来了10000个人,都要找自己住这栋楼的同学时,select版和epoll版宿管大妈,谁的效率更高,不言自明。同理,在高并发服务器中,轮询I/O是最耗时间的操作之一,select和epoll的性能谁的性能更高,同样十分明了。


你可能感兴趣的:(apache与Nginx的主要区别(内核多路I/O之select与epoll的区别))