深入 Nginx: 我们如何设计性能和扩展(2)

在大多数情况下 NGINX 的配置建议:每个 CPU 核心运行一个工作进程,这样最有效地利用硬件资源。你可以在配置中包含 指令配置:

worker_processes auto;

当一个 NGINX 服务处于活动状态,只有工作进程在忙碌。每个工作进程以非阻塞方式保持多连接,以减少上下文交换。

每个工作进程是一个单一线程并且独立运行,它们会获取新连接并处理之。这些进程可以使用共享内存通信来共享缓存数据、会话持久性数据及其它共享资源。(在 NGINX 1.7.11 及其以后版本,还有一个可选的线程池,工作进程可以转让阻塞的操作给它。更多的细节,参见“NGINX 线程池可以爆增9倍性能!”。对于 NGINX Plus 用户,该功能计划在今年晚些时候加入到 R7 版本中。)

NGINX 工作进程内部

深入 Nginx: 我们如何设计性能和扩展

每个 NGINX 工作进程按照 NGINX 配置初始化,并由主进程提供一组监听端口。

NGINX 工作进程首先在监听套接字上等待事件( 和内核套接字分片)。事件被新进来的连接初始化。这些连接被分配到一个状态机 – HTTP 状态机是最常用的,但 NGINX 也实现了流式(原始 TCP )状态机和几种邮件协议(SMTP、IMAP和POP3)的状态机。

Internet Requests

状态机本质上是一组指令,告诉 NGINX 如何处理一个请求。大多数 web 服务器像 NGINX 一样使用类似的状态机来实现相同的功能 - 区别在于实现。

调度状态机

把状态机想象成国际象棋的规则。每个 HTTP 事务是一个象棋游戏。一方面棋盘是 web 服务器 —— 一位大师可以非常迅速地做出决定。另一方面是远程客户端 —— 在一个相对较慢的网络下 web 浏览器访问网站或应用程序。

不管怎样,这个游戏规则很复杂。例如,web 服务器可能需要与各方沟通(代理一个上游的应用程序)或与身份验证服务器对话。web 服务器的第三方模块甚至可以扩展游戏规则。

一个阻塞状态机

回忆我们之前的描述,一个进程或线程就像一套独立的指令集,操作系统可以在一个 CPU 核心上调度运行它。大多数 web 服务器和 web 应用使用每个连接一个进程或者每个连接一个线程的模式来玩这个“象棋游戏”。每个进程或线程都包含玩完“一个游戏”的指令。在服务器运行该进程的期间,其大部分的时间都是“阻塞的” —— 等待客户端完成它的下一步行动。

Blocking I/O

web 服务器进程在监听套接字上监听新连接(客户端发起新“游戏”)

当它获得一个新游戏,就玩这个游戏,每走一步去等待客户端响应时就阻塞了。

游戏完成后,web 服务器进程可能会等待是否有客户机想要开始一个新游戏(这里指的是一个“保持的”连接)。如果这个连接关闭了(客户端断开或者发生超时),web 服务器进程会返回并监听一个新“游戏”。

要记住最重要的一点是每个活动的 HTTP 连接(每局棋)需要一个专用的进程或线程(象棋高手)。这个结构简单容并且易扩展第三方模块(“新规则”)。然而,还是有巨大的不平衡:尤其是轻量级 HTTP 连接其实就是一个文件描述符和小块内存,映射到一个单独的线程或进程,这是一个非常重量级的系统对象。这种方式易于编程,但太过浪费。

NGINX是一个真正的象棋大师

也许你听过同时表演赛游戏,有一个象棋大师同时对战许多对手?

Kiril Georgiev

列夫·吉奥吉夫在保加利亚的索非亚同时对阵360人。他的最终成绩是284胜70平6负。

这就是 NGINX 工作进程如何“下棋”的。每个工作进程(记住 - 通常每个CPU核心上有一个工作进程)是一个可同时对战上百人(事实是,成百上千)的象棋大师。

Event-driven Architecture

工作进程在监听和连接套接字上等待事件。

事件发生在套接字上,并且由工作进程处理它们:

在监听套接字的事件意味着一个客户端已经开始了一局新棋局。工作进程创建了一个新连接套接字。

在连接套接字的事件意味着客户端已经下了一步棋。工作进程及时响应。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/fa2902cef1ab159f8c677de18db49d29.html