Nginx支持大规模并发的工作原理

Nginx在主流硬件上的并发连接能达到数十万,在网络处理方面的领先地位,完全归功于突破性的事件驱动架构设计。

Nginx在每颗内核上都有一个工作进程,可有效利用硬件资源。在单个工作进程内交替处理多个连接,以应对突如其来的网络流量。

Nginx资源管理

Nginx使用状态机管理流量。非阻塞事件驱动架构,能同时调度多个状态机。支持不停机的升级操作。

NGINX进程模型

Nginx有一个主进程,用于执行特权操作,如读取配置和绑定端口,以及多个工作进程和辅助进程。在四核服务器上,Nginx主进程会创建四个工作进程,及若干缓存辅助进程,用于管理磁盘上的内容缓存。

任何Unix应用程序的基础都是线程或进程,在Linux上线程和进程大致相同,区别在于它们共享内存的程度,线程或进程是一组独立的指令,操作系统可以用一颗CPU内核来运行它。大多复杂的应用程序,并行执行多个线程或进程有两个原因:

1、充分利用多核计算,让并行执行操作变容易。

2、充分利用多颗cpu内核进行计算,实现并行操作。

进程/线程对资源的消耗

不管线程还是进程都使用内存和操作系统的资源,并在内核上进行切换(称为上下文切换操作)。大多数现代服务器,能同时处理数百个小的活动线程/进程。一旦内存耗尽或高I/O负载,都将导致大量的上下文切换,应用性能骤降。

网络应用程序设计的常规手段

在做网络应用程序设计时,通常会为每个连接分配一个线程/进程,这种结构简单并易于实现,但无法应对并发需求。

NGINX工作原理

Nginx使用可预测的进程模型调用硬件资源。

可预测的进程模型

主进程:执行特权操作,如读取配置、绑定端口。然后创建少量子进程。

缓存加载器进程:在Nginx启动时运行,将基于磁盘的缓存内容加载到内存中,然后退出,是一个保守地调度,对资源的消耗很低。

工作进程:负责处理网络连接。

缓存管理

管理缓存的进程,会定期地处理磁盘缓存中的条目,将缓存数量控制在配置指定的范围内。

工作进程

工作进程处理网络连接,完成从磁盘上读取内容,及将内容写入到磁盘的工作,并与后端服务进行通信。

Nginx推荐配置

大多数情况下推荐的配置原则:

每颗CPU核心运行一个工作进程,如此,可以最大化地利用硬件资源。通过在worker_processes指令上设置auto参数实现。

worker_processes auto;

在Nginx服务器处于活动状态时,其实,只有工作进程处于繁忙状态。每个工作进程以非阻塞的方式处理多个连接,减少上下文切换。

每个工作进程都是单线程的,进程间使用共享内存进行通信,共享内容包括:缓存数据、持久性会话数据和其他共享资源。

NGINX工作进程

每个工作进程都使用Nginx配置进行初始化,并由主进程提供一组监听的socket。Nginx工作进程首先会等待监听上的socket事件,事件会由新到达的连接启动,连接被分配给状态机。

Nginx状态机本质上是一组告诉Nginx如何处理请求的指令。大多数Web服务器执行这样的功能时,都采用类似的状态机 - 不同之处在于实现。 

注:状态机应用于HTTP比较常见,称为http状态机,Nginx还为原始TCP和邮件协议实现了状态机。

调度状态机

将Nginx调度状态机想像成象棋规则,每个HTTP事务都是一局象棋游戏,在棋盘的一侧是web服务器,一个能够快速做出决定的大师。另一侧是远程客户端,Web浏览器通过相对较慢的网络,访问一个站点或应用。

游戏规则可能非常复杂,如:Web服务器可能需要与后端服务或身份验证服务器通信,Web服务器中的第三方模块甚至可以扩展游戏规则。

阻塞状态机

大多数Web服务器和Web应用,都使用一个进程/线程的连接模型来玩象棋游戏。每个进程/线程都玩到游戏结束为止,在服务器运行该进程期间,它大部分时间都处于“阻塞”状态 - 等待客户端完成下一步操作。

Web服务器进程在监听的socket上等待客户端发起的新游戏,当获得一局新游戏时,就开始该游戏,在每次移动后会被阻塞,以等待客户端的响应。游戏完成后,Web服务器进程可能会等待客户端是否要启动新游戏(对应于keepalive连接)。如果客户端消失或发生超时,则Web服务器进程将返回并监听新游戏。

注:每个活动的HTTP连接,都需要一个专用的进程或线程(一个特级大师)。这种架构很简单,然而,存在巨大的不对称:把相当轻量级的HTTP连接(由文件描述符和少量内存表示),映射到单独的线程/进程(一个非常重量级的操作系统对象)上,这种编程方式很方便,但非常浪费资源。 

NGINX是真正的大师

也许听说过一位国际象棋大师同时有几十个对手,Kiril Georgiev在保加利亚索非亚同时对弈360个人,最终得分是284胜70平6负。Nginx工作进程就是这样的大师,与多人同时对弈。

每个工作进程都是一个可同时,与数十万玩家对战的游戏的大师。

注:每颗CPU上通常只有一个工作进程。

工作进程等待侦听事件,然后建立sockets连接,事件发生,工作进程处理它们。监听上的事件,意味着客户端已经开始了新一轮的游戏,工作进程会创建一个新的sockets连接。sockets连接上的一个事件意味着,客户端移动了一步,工作进程会迅速作出反应。工作进程永远不会被阻塞,以便等待向客户端做出响应,在客户端移动时,工作进程会去其他等待移动的棋局,并随时欢迎新玩家入场。

在这种模式下每个连接几乎没有额外的开销,Nginx进程会持续待在CPU时间片上,上下文切换相对而言就会减少很多,只在没事可做时才发生切换。

更新配置和升级

Nginx架构只有少量的工作进程,更新配置和升级操作都非常方便。更新NGINX配置是一个非常简单、轻便、可靠的操作。通常只需运行nginx -s reload命令,该命令检查磁盘上的配置并向主进程发送SIGHUP信号。当主进程收到SIGHUP时会做两件事:

1.重新加载配置,并创建一组新的工作进程。这些新的工作进程会立即开始接受连接和处理流量。

2.发信号通知旧工作进程正常退出,工作进程停止接受新连接。一旦每个处于连接中的HTTP请求完成,工作进程就会关闭连接,关闭所有连接后,旧工作进程退出。

重新加载的过程,可能会导致CPU和内存使用量出现小幅增长,但与活动连接所消耗的资源相比,它通常难以察觉。可以在一秒内多次重新加载配置,很少有NGINX工作进程在等待连接关闭时出现问题,即使有这样的问题也会很快得到解决。

Nginx的升级过程实现了高可用性的Holy Grail  - 可以即时升级软件,不会出现任何连接丢失,停机或服务中断。

Nginx升级过程与正常重新加载配置的方法类似,新的Nginx主进程与原始主进程并行运行,它们共享侦听。两个进程都处于活动状态,它们各自的工作进程都会处理流量。然后,通过发信号通知原始主进程,及其工作进程优雅的退出。

Nginx借助操作系统实现IO多路复用(I/O multiplexing),因此,性能直接与所处在的操作系统挂钩。