Nginx 高速并发处理模型
2020-02-23 01:28:33
皮皮鲁
Nginx
高速并发处理模型
一
概述
Nginx
是一款高性能webserver
软件。除此以外它还具有很强的负载均衡,反向代理,邮件代理以及静态缓存的功能。在提供这些功能的同时,Nginx
通过优秀的架构设计,实现了高速的高并发处理。
这篇文章我们通过分析Nginx
的框架结构,来解释它的高速并发处理的背后原因。
二
框架结构
如下图所示,
Nginx
结合采用多进程和
IO
多路复用的结构处理并发客户请求。
Master
进程主要负责信号处理,监控和管理
Worker
进程。
Master
进程本身不处理具体业务。
Worker
进程处理具体业务,包括处理连接请求和网络读写事件的处理。多个
worker
进程可以独立地处理各自的客户连接。
Worker
进程之间通过信号量和共享内存进行通信。
通过配置与系统
CPU
等量的
worker
进程,可以实现某一个进程绑定某一个特定
CPU
从而减少系统开销。
在每一个
worker
进程处理多个
client
并发连接请求时,
Nginx
采用
IO
多路复用技术在一个进程内同时处理多个
client
的网络读写事件。与多进程
/
线程处理多连接请求模型相比,
IO
多路复用可以减少大量的进程调度带来的系统开销,从而提高系统整体的处理性能。
下面我们来进一步研究
Nginx
采用的多进程和
IO
多路复用机制。
三
并发连接请求处理方法
1
并发处理方式

网络服务器在处理并发处理方式,如上图socket
处理流程所示,
根据
accept
得到新连接以后是启用新的进程
/
线程还是直接在原来本进程内处理,
分为如下两种方式。
一种是多进程/
线程方式,这种方式为每一个新来的连接生成一个新的进程/
线程来单独处理。
另外一种是IO
多路复用,这种方式在一个进程/
线程中维护所有的连接的socket,
通过事件处理的方式依次处理所有连接上的网络事件。
多线程模型如下:
IO
多路复用模型如下:
2
多进程
/
线程模型
2.1
模型种类
在多进程 / 线程的模型中,根据 accept 在新老进程中的位置又分为两种。一种类型是 accept 在父进程中进行,每次 accept 以后, fork 一个新进程或者创建一个新线程来处理新 accept 的连接。一种类型是父进程在 listen 调用以后, fork 出多个进程或者创建多个线程分别进行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
个连接。

Select
和Poll
需要经历维护等待队列和堵塞进程两个阶段。在支持大量socket
处理的场景下,效率比较低。主要原因在于select
的执行过程有如下三次遍历链表操作。
1.
进程调用select/poll
时,会被加入到所有需要处理的socket
的等待队列里。
2.
任何一个socket
有数据到来,就会把进程唤起。同时遍历所有的socket,
把进程从这些socket
等待队列中移除。
3.
进程被唤起运行以后,需要再次遍历所有监听的socket,
依次去判断哪些socket
有数据要处理。
处理完毕以后,再次调用select
时,需要重复步骤1.
这种实现方式,在处理大量连接时,socket
链表会比较长,处理过程中的三次循环会非常消耗时间。
3.3 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
的热度,进而提高了整体系统的并发处理能力。

发布评论 加入社群
宗兆伟
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

回复评论
发布评论