面试之Nginx的epoll的优势

目录

面试时被问到:是否了解 Nginx,它使用的 epoll 模式和其他的相比有什么优势?

直接被问住,实际生产中配过不少的 Nginx,各种 rewrite、regex、正反向代理、php、fast-cgi、限流、证书、jwt、cors;epoll 只大概有印象是下面这行:

1events {
2    use epoll;
3    worker_connections  1024;
4}

实在是汗颜啊,所以得仔细研究一下这个 epoll。

首先扔概念

IO多路复用:

多路是指网络连接,复用指的是同一个线程。

IO多路复用是一种同步IO模型,实现一个线程可以监视多个文件描述符;

一旦某个描述符就绪(一般是读就绪或者写就绪),就能够通知应用程序进行相应的读写操作;

没有文件描述符就绪时会阻塞应用程序,交出cpu。

那么IO多路复用的实现方法有三种: select、poll、epoll

select、poll、epoll本质上都是同步I/O,用户进程(这里就是Nginx)负责读写(从内核空间拷贝到用户空间),读写过程中,用户进程是阻塞的。

select:

  • 查询 fd_set 中,是否有就绪的 fd,可以设定一个超时时间,当有 fd (File descripter) 就绪或超时返回;

  • fd_set 是一个位集合,大小是在编译内核时的常量,默认大小为 1024

  • 特点:

  • 连接数限制,fd_set 可表示的 fd 数量太小了;

  • 线性扫描:判断 fd 是否就绪,需要遍历一边 fd_set;

  • 数据复制:从内核空间拷贝到用户空间,复制连接就绪状态信息

poll:

  • 解决了连接数限制:

  • poll 中将 select 中的 fd_set 替换成了一个 pollfd 数组

  • 解决 fd 数量过小的问题

  • 数据复制:从内核空间拷贝到用户空间,复制连接就绪状态信息

epoll:event 事件驱动

  • 事件机制:避免线性扫描

  • 为每个 fd,注册一个监听事件

  • fd 变更为就绪时,将 fd 添加到就绪链表

  • fd 数量:无限制(OS 级别的限制,单个进程能打开多少个 fd)

区别在于:

epoll较灵活,如果有一百万个链接状态同时保持,但是在某个时刻,只有几百个链接是活跃的。epoll的处理是通过epoll_create()创建对象,epoll_ctl()收集所有的套接字添加到epoll对象,epoll_wait()收集所有发生事件也就是所谓的活跃的链接,并收集到一个List链表中,这样只需要遍历这些List链表里的数据,而不用遍历一百万个链接。 而后者select poll则是每次收集事件时,将这一百万个链接都传给操作系统,再由操作系统内核上判断某些链接产生了事件,造成了巨大的资源浪费(大批量的不同态内存复制)。

为什么epoll效率比较高

epoll 在epoll_create 时,就已经建立好了一个文件描述符对应 epoll 对象,同时,在内核 cache 里建立了一个红黑树,用于存储后续epoll_ctl传来的socket连接。再同时,又建立了一个list链表,用于存储准备就绪的事件。 等到 epoll_wait() 调用的时候,只需要观察list链表里有没有数据就可以,有数据就返回,没数据就sleep。等到timeout后,即使没数据,也返回了。所以 epoll_wait 会非常高效

三者的比较:

select poll epoll
数据结构 bitmap 数组 红黑树
最大连接数 1024 无上限 无上限
fd拷贝 每次调用select拷贝 每次调用poll拷贝 fd首次调用epoll_ctl拷贝,每次调用epoll_wait不拷贝
工作效率 轮询:O(n) 轮询:O(n) 回调:O(1)

Nginx 的并发处理能力

关于 Nginx 的并发处理能力:

  • 并发连接数,一般优化后,峰值能保持在 1~3w 左右。(内存和 CPU 核心数不同,会有进一步优化空间)
  • Nginx 的最大连接数:Worker 进程数量 x 单个 Worker 进程的最大连接数

Kubernetes下Flannel网络
面试之Docker如何打出最小的镜像
comments powered by Disqus