NGINX在网络性能方面处于领先地位,这全都归功于该软件的设计方式。尽管许多Web服务器和应用程序服务器使用简单的基于线程或基于进程的体系结构,但NGINX凭借复杂的事件驱动体系结构而脱颖而出,使其能够扩展到现代硬件上成千上万的并发连接。
的内部NGINX信息图钻头从高级别处理架构下以说明NGINX如何处理一个单一的过程中的多个连接。该博客详细解释了所有工作原理。
设置场景– NGINX流程模型
为了更好地理解此设计,您需要了解NGINX的运行方式。NGINX有一个主进程(执行特权操作,如读取配置和绑定到端口)以及许多辅助进程。
# service nginx restart
* Restarting nginx
# ps -ef --forest | grep nginx
root 32475 1 0 13:36 ? 00:00:00 nginx: master process /usr/sbin/nginx
-c /etc/nginx/nginx.conf
nginx 32476 32475 0 13:36 ? 00:00:00 _ nginx: worker process
nginx 32477 32475 0 13:36 ? 00:00:00 _ nginx: worker process
nginx 32479 32475 0 13:36 ? 00:00:00 _ nginx: worker process
nginx 32480 32475 0 13:36 ? 00:00:00 _ nginx: worker process
nginx 32481 32475 0 13:36 ? 00:00:00 _ nginx: cache manager process
nginx 32482 32475 0 13:36 ? 00:00:00 _ nginx: cache loader process
在这台四核服务器上,NGINX主进程创建了四个工作进程和几个缓存帮助程序,用于管理磁盘内容缓存。
为什么架构很重要?
任何Unix应用程序的基本基础都是线程或进程。(从Linux OS的角度来看,线程和进程几乎是相同的;主要区别在于它们共享内存的程度。)线程或进程是操作系统可以调度在CPU上运行的一组独立的指令。核。大多数复杂的应用程序并行运行多个线程或进程,其原因有两个:
- 他们可以同时使用更多的计算核心。
- 线程和进程使并行操作变得非常容易(例如,同时处理多个连接)。
进程和线程消耗资源。它们每个都使用内存和其他OS资源,并且需要在内核之间进行交换(称为上下文切换)。大多数现代服务器可以同时处理数百个小型活动线程或进程,但是一旦内存耗尽或高I / O负载导致大量上下文切换,性能就会严重下降。
设计网络应用程序的常用方法是为每个连接分配线程或进程。该体系结构简单易实现,但是当应用程序需要处理数千个同时连接时,它无法扩展。
NGINX如何工作?
NGINX使用可预测的过程模型,该模型已调整为可用的硬件资源:
- 的Master主过程执行特权操作,例如读取配置和绑定到端口,然后创建一个小的数目的子进程(接下来的三个类型)。
- 缓存加载器进程在启动时运行,以将基于磁盘的缓存加载到内存中,然后退出。它的计划很保守,因此其资源需求较低。
- 该高速缓存管理器进程运行从磁盘缓存定期和子条目,让他们配置的大小范围之内。
- 该Worker进程做所有的工作!它们处理网络连接,将内容读取和写入磁盘,并与上游服务器进行通信。
在大多数情况下,建议使用NGINX配置(每个CPU内核运行一个工作进程),以最有效地利用硬件资源。您可以通过auto在worker_processes伪指令上设置参数来配置它:
worker_processes auto;
当NGINX服务器处于活动状态时,仅工作进程处于繁忙状态。每个工作进程以非阻塞方式处理多个连接,从而减少了上下文切换的数量。
每个工作进程都是单线程的,并且独立运行,以获取新连接并进行处理。进程可以使用共享内存进行通信,以共享高速缓存数据,会话持久性数据和其他共享资源。
在NGINX Worker流程内部
每个NGINX工作进程都使用NGINX配置初始化,并且由主进程提供一组侦听套接字。
NGINX工作进程从等待侦听套接字上的事件开始(accept_mutex和内核套接字分片)。事件由新的传入连接启动。这些连接被分配给状态机 -HTTP状态机是最常用的,但是NGINX还为流(原始TCP)流量和许多邮件协议(SMTP,IMAP和POP3)实现状态机。
状态机本质上是一组指令,用于告诉NGINX如何处理请求。大多数执行与NGINX相同功能的Web服务器都使用类似的状态机-区别在于实现。
调度状态机
将状态机想像成国际象棋的规则。每个HTTP事务都是一个国际象棋游戏。棋盘的一侧是Web服务器-一个可以非常快速地做出决策的大师。另一端是远程客户端,即通过相对较慢的网络访问站点或应用程序的Web浏览器。
但是,游戏规则可能非常复杂。例如,Web服务器可能需要与其他方通信(代理上游应用程序)或与身份验证服务器对话。Web服务器中的第三方模块甚至可以扩展游戏规则。
阻塞状态机
回顾一下我们对进程或线程的描述,它们是操作系统可以计划在CPU内核上运行的一组独立的指令。大多数Web服务器和Web应用程序都使用每个连接进程或每个连接线程模型来下棋游戏。每个进程或线程都包含从头至尾玩一个游戏的指令。在服务器运行该过程的过程中,它大部分时间都处于“阻塞”状态–等待客户端完成下一步操作。
- Web服务器进程在侦听套接字上侦听新的连接(客户端发起的新游戏)。
- 当它获得新游戏时,它会玩该游戏,在每次移动后等待客户响应都将阻塞。
- 游戏结束后,Web服务器进程可能会等待查看客户端是否要开始新游戏(这与保持连接状态相对应)。如果连接已关闭(客户端消失或发生超时),则Web服务器进程将返回以侦听新游戏。
要记住的重要一点是,每个活动的HTTP连接(每个国际象棋游戏)都需要专用的进程或线程(特级大师)。该体系结构简单易用第三方模块(“新规则”)进行扩展。但是,这存在巨大的不平衡:由文件描述符和少量内存表示得相当轻量级的HTTP连接映射到单独的线程或进程,这是一个非常重的操作系统对象。这是编程上的便利,但是非常浪费。
NGINX是真正的大师
也许您听说过同时进行的展览游戏,其中一位国际象棋大师同时玩几十个对手?
Kiril Georgiev在保加利亚的索非亚同时和360人下棋。他的最终得分是284胜70平6负。
这就是NGINX工作进程扮演“棋子”的方式。每个工作人员(记住-每个CPU内核通常只有一个工作人员)是一位大师,可以同时玩数百(实际上是数十万)个游戏。
- Worker工作单元等待侦听和连接套接字上的事件。
- 事件在套接字上发生,并且工作程序处理它们:监听套接字上的事件表示客户端已开始新的国际象棋游戏。Worker创建一个新的连接套接字。连接套接字上的事件表示客户端已采取新措施。Worker及时作出反应。
- Worker从不阻塞网络流量,而等待其“对手”(客户端)做出响应。动作完成后,Worker会立即进入其他等待处理动作的游戏,或者欢迎新玩家加入游戏。
为什么这比阻塞的多进程体系结构要快?
NGINX很好地扩展以支持每个工作进程数十万个连接。每个新连接都会创建另一个文件描述符,并在工作进程中消耗少量额外的内存。每个连接几乎没有额外的开销。NGINX进程可以保持固定在CPU上。上下文切换相对较少,并且在没有工作要做时会发生。
在阻塞式每进程连接方法中,每个连接都需要大量的额外资源和开销,并且上下文切换(从一个进程交换到另一个进程)非常频繁。
有关更详细的说明,请查看NGINX,Inc.公司开发副总裁兼联合创始人Andrew Alexeev撰写的有关NGINX体系结构的文章。
通过适当的系统调整,NGINX可以扩展为在每个工作进程中处理数十万个并发HTTP连接,并且可以吸收流量峰值(大量新游戏涌入)而不会错过任何机会。
更新配置并升级NGINX
NGINX的进程体系结构具有少量的工作进程,因此可以非常有效地更新配置,甚至可以更新NGINX二进制文件本身。
更新NGINX配置是一个非常简单,轻巧且可靠的操作。它通常仅意味着运行nginx -s reload命令,该命令检查磁盘上的配置并向主进程发送SIGHUP信号。
当主进程接收到SIGHUP时,它将执行以下两项操作:
- 重新加载配置并派生一组新的工作进程。这些新的工作进程立即开始接受连接和处理流量(使用新的配置设置)。
- 指示旧工作进程正常退出。工作进程停止接受新连接。当前的每个HTTP请求完成后,工作进程就会干净地关闭连接(即,没有持久的keepalive)。一旦所有连接都关闭,工作进程将退出。
此重新加载过程可能会导致CPU和内存使用量的小幅上升,但是与从活动连接中加载资源相比,这通常是不可察觉的。您可以每秒多次重载配置(许多NGINX用户正是这样做的)。很少有问题出现,当有许多代NGINX工作进程等待连接关闭时,即使这些连接很快被解决。
NGINX的二进制升级过程实现了高可用性的圣杯–您可以动态升级软件,而不会出现连接断开,停机或服务中断的情况。
二进制升级过程与正常重载配置的方法类似。一个新的NGINX主进程与原始主进程并行运行,并且它们共享侦听套接字。这两个进程都处于活动状态,并且它们各自的工作进程都处理流量。然后,您可以向旧的主机及其工作人员发出信号,使其优雅地退出。
整个过程在“控制NGINX”中有更详细的描述。
结论
该内部NGINX信息图表提供的NGINX如何功能的高度概括,但是这背后简单的解释是,经过十多年的创新和优化,使NGINX对范围广泛的硬件提供最佳的性能,同时保持安全性和可靠性现代Web应用程序要求。
(本文由闻数起舞翻译自Owen Garrett 的文章《Inside NGINX: How We Designed for Performance & Scale》,转载请注明出处,原文链接:
https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/)