原创

Nginx高并发原理简析

温馨提示:
本文最后更新于 2023年01月31日,已超过 448 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

一、背景概述

Nginx是一款轻量级的Web服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,其特点是占有内存少、并发能力强,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名,也由此受到了很多大厂的青睐。

那么Nginx是如何实现的高并发的呢?下面将为大家分享一下Nginx架构设计,探究一下Nginx备受青睐的秘诀在哪里

二、Nginx的进程结构

Nginx其实有两种进程结构,一种是单进程结构,一种是多进程结构。在我们实际生产环境中,由于需要保持nginx的健壮性,因此多进程结构较为实用且常见。

为什么是多进程结构,而不是多线程结构?

首先我们来看一下多进程与多线程的对比:

Nginx最核心的一个目的是要保持高可用性、高可靠性,在采用多进程结构时,由于和进程间不会相互影响,单个进程出现异常并不会导致整个Nginx服务挂掉。同时,多个worker进程可以分别占用不同的CPU核心来工作,提高了Nginx处理请求的速度。另外,采用多进程模型还可以通过进程间通信来实现负载均衡,即一个请求到来时更容易被分配到负载较轻的worker工作进程中处理,这也同样提高了Nginx的请求处理能力。

如上图所示,Nginx在启动后,会有一个master进程和多个worker进程。

master进程主要用来做worker进程的管理,包含:接收来自外界的信号、向各worker进程发送信号、监控worker进程的运行状态、Nginx配置文件重新载入等。worker进程,才是处理真正请求的工作进程。

cache(缓存)是在多个worker进程间共享的,而且缓存不仅要被worker进程使用,还要被cachemanager 和cacheloader进程使用。cachemanager 和cacheloader 也是为反向代理时,后端发来的动态请求做缓存所使用的,cacheloader 只是用来做缓存的载入、cachemanager 用来做缓存的管理。实际上每个请求处理时,使用到缓存还是由worker进程来进行的。

这些进程间的通讯,都是使用共享内存来解决的。可以看到cachemanager 和cacheloader各有一个进程,master进程因为是父进程,所以肯定只有一个。那么worker进程为什么会有很多呢?这是因为Nginx采用了事件驱动引擎以后,他希望每一个worker进程从头到尾占有一颗CPU,所以往往不止要把worker进程的数量配置与我们服务器上的CPU核数一致以外,还需要把每一个worker进程与某一颗CPU核绑定在一起,这样可以更好的使用每颗CPU核上面的CPU缓存来减少缓存失效的命中率。

三、Nginx的异步非阻塞处理机制

基本概念:

异步、同步

同步和异步,这两个概念是从客户端与服务器之间的通信交互方式得来的。

同步,指客户端向服务器发送一个请求后、必须接收到服务器对此请求的响应后,才会再向服务器发送下一个请求。

异步,指客户端向服务器发送一个请求后,并不等待服务器对此请求的响应,便发送了下一个请求。

阻塞、非阻塞

阻塞和非阻塞,这两个概念是从服务器内部处理请求的方式来说的。

阻塞,指服务器接收到请求后,如果遇到IO阻塞,当前线程会被挂起,直到IO完成后唤醒当前线程,当前线程期间不会去处理其他事情。

非阻塞,指服务器接收到请求后,如果遇到IO阻塞,当前线程不会挂起,而是会立即返回去执行下一个调用。

同步与异步,重点在于消息通知的方式;阻塞与非阻塞,重点在于等消息时候的行为。

所以,就有了下面4种组合方式:

同步阻塞:小明收到信息后,啥都不干,等快递;

同步非阻塞:小明收到信息后,边刷微博,边等着取快递;

异步阻塞:小明收到信息后,啥都不干,一直等着快递员通知他取快递;

异步非阻塞:小明收到信息后,边刷着微博,边等快递员通知他取快递。

Nginx之所以可以支持高并发,就是因为Nginx用的是上述所讲的异步非阻塞的机制,而Nginx是靠事件驱动模型来实现这种机制的。

在Nginx的事件驱动模型下,客户端发起的所有请求在服务端都会被标记为一个事件,Nginx会把这些事件收集到“事件收集器”里,然后再把这些事件交给内核去处理。

四、Nginx的事件驱动模型

Nginx的事件驱动模型,支持select、poll、epoll、rtsig、kqueue、/dev/poll、eventport等,最常用的是前三种。

Select模型:首先会创建所关注事件的描述符集合。会分别创建读(Read)事件、写(Write)事件、异常发生(Exception)事件三类描述符集合来收集三类描述符。然后调用底层提供的select()函数,等待事件发生。最后轮询所有事件描述符集合中的每一个描述符,检查是否有相应的事件发生,然后处理事件。

Poll模型:其与select的基本实现方式相同,只不过创建关注的描述符集合时,不分成三个集合,而是一个集合包括所有描述符。

Epoll模型:当描述符比较多时,遍历描述符集合、然后查找每个描述符是否有相应事件发生这一过程会效率较低。epoll选择将描述符列表的管理交给内核复制,一旦有事件发生,内核会将有事件发生的描述符列表通知给进程,这样避免了应用程序轮询整个描述符列表。epoll会通过相关调用,通知内核创建一个N个描述符的事件列表,然后给这些描述符设置所关注的事件,并把他添加到内核的事件列表中,在编码过程中也可以通过相关调用对事件列表中的描述符进行动态地删除和修改。

五、总结

Nginx是如何实现高并发的?

如果一个server采用一个进程负责一个request的方式,那么进程数就是并发数。正常情况下,会有很多进程一直在等待中。而nginx采用一个master进程,多个worker进程的模式。master进程主要负责收集、分发请求。每当一个请求过来时,master就拉起一个worker进程负责处理这个请求。同时master进程也负责监控worker的状态,保证高可靠性。worker进程一般设置为跟cpu核心数一致。nginx的worker进程在同一时间可以处理的请求数只受内存限制,可以处理多个请求。Nginx的异步非阻塞工作方式正把当中的等待时间利用起来了。在需要等待的时候,这些进程就空闲出来待命了,因此表现为少数几个进程就解决了大量的并发问题。每进来一个request,会有一个worker进程去处理。但不是全程的处理,处理到什么程度呢?处理到可能发生阻塞的地方,比如向上游(后端)服务器转发request,并等待请求返回。那么,这个处理的worker很聪明,他会在发送完请求后,注册一个事件:“如果upstream返回了,告诉我一声,我再接着干”。于是他就休息去了。此时,如果再有request进来,他就可以很快再按这种方式处理。而一旦上游服务器返回了,就会触发这个事件,worker才会来接手,这个request才会接着往下走。

正文到此结束
本文目录