F5社区-F5技术交流中心

Nginx 高速并发处理模型

2020-02-23 01:28:33

皮皮鲁

Nginx 高速并发处理模型

 

概述  

 

Nginx 是一款高性能webserver 软件。除此以外它还具有很强的负载均衡,反向代理,邮件代理以及静态缓存的功能。在提供这些功能的同时,Nginx 通过优秀的架构设计,实现了高速的高并发处理。

 

这篇文章我们通过分析Nginx 的框架结构,来解释它的高速并发处理的背后原因。

 

框架结构

 

如下图所示, Nginx 结合采用多进程和 IO 多路复用的结构处理并发客户请求。         

 

Master 进程主要负责信号处理,监控和管理 Worker 进程。 Master 进程本身不处理具体业务。 Worker 进程处理具体业务,包括处理连接请求和网络读写事件的处理。多个 worker 进程可以独立地处理各自的客户连接。 Worker 进程之间通过信号量和共享内存进行通信。


1  Nginx 整体架构

 

通过配置与系统 CPU 等量的 worker 进程,可以实现某一个进程绑定某一个特定 CPU 从而减少系统开销。

 

在每一个 worker 进程处理多个 client 并发连接请求时, Nginx 采用 IO 多路复用技术在一个进程内同时处理多个 client 的网络读写事件。与多进程 / 线程处理多连接请求模型相比, IO 多路复用可以减少大量的进程调度带来的系统开销,从而提高系统整体的处理性能。

 

下面我们来进一步研究 Nginx 采用的多进程和 IO 多路复用机制。


并发连接请求处理方法

 

1 并发处理方式

2   网络 socket 编程流程

网络服务器在处理并发处理方式,如上图socket 处理流程所示, 根据 accept 得到新连接以后是启用新的进程 / 线程还是直接在原来本进程内处理, 分为如下两种方式。

 

一种是多进程/ 线程方式,这种方式为每一个新来的连接生成一个新的进程/ 线程来单独处理。

 

另外一种是IO 多路复用,这种方式在一个进程/ 线程中维护所有的连接的socket, 通过事件处理的方式依次处理所有连接上的网络事件。

 

多线程模型如下:


3 多进程 / 线程模型

IO 多路复用模型如下:


4  IO 多路复用模型

2 多进程 / 线程模型

 

2.1 模型种类

 

在多进程 / 线程的模型中,根据 accept 在新老进程中的位置又分为两种。一种类型是 accept 在父进程中进行,每次 accept 以后, fork 一个新进程或者创建一个新线程来处理新 accept 的连接。一种类型是父进程在 listen 调用以后, fork 出多个进程或者创建多个线程分别进行accept。


5  accept 后创建进程 / 线程


6 新进程 / 线程里执行 accept

 

除此以外,随着 Linux3.9 版本对 SO_REUSEPORT 模式的支持,可以允许多个进程同时绑定在同一个地址的相同的 port 上。所以,下面的模型也开始被 Nginx 支持。


     7  SO_REUSEPORT 模型

2.2 三种进程/ 线程模型比较

 

第一种模型 accept 以后再 fork 一个新的进程 / 线程处理连接,在大流量并发的情况下,主进程需要处理所有的连接请求,在大量并发新连接环境下, accept 主进程会成为瓶颈。

 

第二种模型,解决了第一种模型中的 accept 瓶颈问题。每一个进程 / 线程都可以处理连接请求。但是又引入了 网络惊群 的问题。这个问题就是,当有一个新的连接到来时,所有准备 accept 的进程 / 线程都会被唤起去抢夺这个新的连接。为了解决这个问题, Nginx Linux2 .6之前的版本引入了一个 accept mutex 文件锁,各个 worker 进程竞争这个 accetp mutex 。新的连接会被持有 accept mutex 的进程处理。 Linux2.6 内核已经有效解决了 网络惊群 的问题。可以通过配置来选择是否需要 accept mutex 机制。

 

第三种模型是建立 Linux 3.9 内核支持 SO_REUSEPORT 选项的基础上。这种模型本身不再有“网络惊群”问题。通过这个选项,可以允许多个进程同时监听同一个地址和端口。当有新的连接到来时,内核会选择某一个进程进行唤起,从而在内核级别把新连接请求相对公平地分配到不同的 worker 进程中。

 

多进程 / 线程的处理模型,在高并发的情况下,需要大量的进程 / 线程来处理连接,系统负担会比较大。在多线程模型中,因为多个线程共享系统资源,一个线程出问题整个系统都会出现问题,因此,稳定性比较差。

 

Nginx 可以通过配置文件(有的选项需要内核支持),采用第二种或者第三种模型。在第二种模型中,也可以通过配置选择是否使用 accept mutex 解决网络惊群问题。

 

3 IO 多路复用模型

 

IO 多路复用, 又称为 event driven IO 就是在同一个进程内同时处理多路 IO, 进而提高系统吞吐量。一般是通过维护一个连接池,当某个连接有数据到达,通知进程来进行数据处理。

 

3.1 多路复用的方式

 

Nginx 支持多种并发连接请求,比如 select,poll,epoll,kqueue( 针对 bsd) 等等,这些请求可以通过配置文件进行选择。一般在 linux epoll 的效率要比 select,poll 高很多。

 

3.2 select/poll 原理

 

Select Poll 方式大体原理一样,区别在于Poll 支持同时处理的socket 数量更多。Select 一般最多支持同时处理1024 个连接。

8  Select 实现数据结构

Select Poll 需要经历维护等待队列和堵塞进程两个阶段。在支持大量socket 处理的场景下,效率比较低。主要原因在于select 的执行过程有如下三次遍历链表操作。

 

1.      进程调用select/poll 时,会被加入到所有需要处理的socket 的等待队列里。

2.      任何一个socket 有数据到来,就会把进程唤起。同时遍历所有的socket, 把进程从这些socket 等待队列中移除。

3.      进程被唤起运行以后,需要再次遍历所有监听的socket, 依次去判断哪些socket 有数据要处理。

处理完毕以后,再次调用select 时,需要重复步骤1.

 

这种实现方式,在处理大量连接时,socket 链表会比较长,处理过程中的三次循环会非常消耗时间。

 

3.3 epoll 原理

 

9  epoll 实现数据结构

 

如上图所示,epoll 相对于select, socket 和用户进程之间加入了一个eventPoll rdlist 结构。

 

eventPoll 本身是一个文件句柄, 通过Add/Del 等操作把socket 按照红黑树的数据结构加入或者移除到eventPoll 中。

 

rdlist 是一个就绪队列,当某一socket 有数据到达时,内核一方面唤醒用户进程,另一方面把自身加入到rdlist 中。

 

这样,用户进程在得到运行时,在rdlist 中就可以得到所有的有数据的socket, 而不需要像select 那样遍历所有的socket 列表来查找有数据需要处理的socket epoll 的操作有效的避免了select 操作的三次循环操作,因此很好地提高了并发运行的效率。

 

总结

 

综上所述, Nginx 通过本身优秀的框架设计,加上内核对并行网络处理的支持,得到了非常好的并发处理性能。

 

下列两幅图分别表示在系统层面,一个单队列的网卡和多队列网卡在Linux 系统中,是如何最大限度地保证客户并发处理的并行处理。

 

通过把某些client 的请求被特定的CPU 上面的特定Nginx worker 进程处理的,最大限度地减少了资源共享和CPU 进程切换从而保证了CPU Cache 的热度,进而提高了整体系统的并发处理能力。

 

10   单队列网卡处理结构


11   多队列网卡处理结构

发布评论 加入社群

发布评论

宗兆伟 2020-02-23 17:13:19 0

good!

Olvan 2020-03-04 08:35:02 0

原理分析非常Good。能有些开源软件高并发性能数据比较吗?

相关文章

博文精选 | 《关于规范金融业开源技术应用与发展的意见》对金融业创新发展和开源治理的启示

F5小安

2022-02-24 10:14:36 435

Login

手机号
验证码
© 2019 F5 Networks, Inc. 版权所有。京ICP备16013763号-1