[译]深入 NGINX: 为性能和扩展所做之设计
来自:http://ifeve.com/inside-nginx-how-we-designed-for-performance-scale/
这篇文章写了nginx的设计,写的很仔细全面, 同时里面好多链接的文章也是值得多看一看
原文链接:Inside NGINX: How We Designed for Performance & Scale 翻译:丁一
NGINX在web性能上的表现尤为出众,这完全得益于其设计方式,许多web和应用服务器都是基于线程或进程这种简单的架构,NGINX用了一种精妙的事件驱动架构,在现代的硬件上,它可以处理成千上万的并发连接。
Inside NGINX中的信息图对高级别的进程架构和NGINX如何在单个进程中处理多个连接进行了深入探讨。本文更进一步地阐述了NGINX的所有工作原理。
背景——NGINX进程模型

要更好的理解这个设计,需要熟悉NGINX的运行过程。NGINX有一个主进程(该进程执行一些特权操作,例如读取配置以及绑定端口)以及若干worker进程和helper进程。

在这个4核服务器上,NGINX主进程创建了4个worker进程以及一对用来管理磁盘内容缓存的缓存helper进程。
架构为什么重要?
任何Unix应用的基础都是线程或进程。(从Linux操作系统的角度看,线程和进程几乎是一样的;最大的区别是内存共享的度。)一个线程或进程是一组自包含的指令,这些指令可由操作系统调度到某个CPU核心上运行。大部分复杂应用并行运行多个线程或进程一般有两个原因:
- 可以同时使用多个CPU核心。
- 线程和进程让并行操作变得简单(例如,同时处理多个连接)。
进程和线程会消耗资源。每个进程或线程都会使用内存以及其他操作系统资源,他们都需要切换CPU(称作上下文切换)。大部分现代的服务器都能同时处理几百个小的、活动的线程或进程,但是,一旦内存耗尽或是遇到高I/O负载导致大量上下文切换时性能就会急剧下降。
常规的网络应用设计都是为每个连接分配一个线程或进程。这种架构简单且容易实现,但是,当应用需要同时处理成千上万的连接时,扩展性就不好了。
NGINX是怎么运行的?
NGINX用了一个可预测的进程模型,支持众多硬件:
- 主进程执行一些特权操作,比如读取配置以及绑定端口,然后创建少数子进程(下面的三种类型)。
- 缓存加载进程在启动时运行,用于将磁盘上的缓存加载到内存中,随后退出。对这个进程的调度很保守,所以其资源需求比较低。
- 缓存管理进程会周期性地运行,从磁盘缓存中删除条目以保证缓存没有超过配置的大小。
- worker进程做了所有的工作!它们处理网络连接,从磁盘读取内容或往磁盘中写入内容,以及与上游服务器通信。
大部分场景中推荐的NGINX配置是 —— 每个CPU核心运行一个worker进程 —— 以充分利用硬件资源。在配置中加入worker_processes auto指令即可:
worker_processes auto;
当NGINX服务器活动时,只有worker进程是处于繁忙状态的。每个worker进程以非阻塞的方式处理多个连接,这减少了上下文切换的次数。
每个worker进程都是单线程的并且是独立运行的,它们捕获新的连接然后进行处理。进程之间的共享缓存数据、会话持久数据以及其它共享资源的通信通过共享内存实现。
深入理解NGINX Worker进程

每个worker进程都是用NGINX配置进行初始化的,并且由主进程提供了一组监听套接字。
NGINX worker进程从等待监听套接字上的事件开始(accept_mutex和内核套接字切分(kernel socket sharding))。事件由新进来的连接进行初始化。这些连接被分配给一个状态机 —— HTTP状态机是最常用的,但NGINX也为流(原始TCP)流量以及一些邮件协议(SMTP,IMAP和POP3)实现了状态机。

状态机本质上是一组指令,由它们告诉NGINX如何处理请求。大部分执行与NGINX相同方法的web服务器也用的类似的状态机 —— 区别在于实现。
状态机的调度
将状态机想象成象棋规则。每个HTTP事务就是一盘象棋游戏。棋盘的一侧是web服务器 —— 一个可以快速做决定的象棋大师。另一侧是远程客户端 —— 正在相对较慢的网络中访问站点或应用的web浏览器。
然而,游戏的规则可能会非常复杂。比如,web服务器也许要与其它方(代理到上游应用)进行交流或是要与认证服务器对话。web服务器中的第三方模块甚至还可能扩展游戏规则。
阻塞模式的状态机
前面我们提到,一个线程或进程是一组自包含的指令,这些指令可由操作系统调度到某个CPU核心上运行。大部分web服务器以及web应用使用的是每个连接分配一个进程或每个连接分配一个线程的模式来处理的。每个进程或线程都包含了从开始到结束需要执行的指令。在服务器运行进程期间,大部分时间都是“阻塞的” —— 等待客户端完成其下一个动作。

- web服务器进程在监听套接字上监听新的连接(由客户端初始化的新游戏)。
- 当新游戏准备好后,就开始游戏,每个动作之后都会阻塞,等待客户端的响应。
- 一旦游戏结束,web服务器进程可能还要等等看是否这个客户端需要发起一轮新的游戏(这相当于keepalive连接)。如果连接被关闭(客户端离开或超时),web服务器进程返回去监听新的游戏请求。
关键的一点在于每个活动的HTTP连接(每盘象棋游戏)都需要一个专门的进程或线程(一个象棋大师)。这种架构在扩展第三方模块(“新的规则”)时非常简单方便。然而,存在一个巨大的失衡问题:相当轻量级的HTTP连接,本由一个文件描述符和少量的内存来表示,却映射到了一个单独的线程或进程这种非常重量级的操作系统对象。编程是便利了,但却是个很大的浪费。
NGINX是真大师
也许你已经听过simultaneous exhibition游戏,一个象棋大师同时与几十个对手对战。

这就是NGINX worker进程下“象棋”的方式。每个worker进程(记住 —— 通常是每个CPU核心一个worker进程)都是一个大师,可以同时处理几百盘(实际上是成千上万)游戏。

- worker进程等待监听和连接套接字上的事件。
- 套接字上发生事件后,worker进程开始进行处理:
- 监听套接字上的事件意味着有个客户端发起了一盘新的象棋游戏。worker进程创建出一个新的连接套接字。
- 连接套接字上的事件意味着客户端开始有新的动作了。worker进程就迅速响应。
worker进程永远不会因为网络拥堵而阻塞来等待“对手”(客户端)的响应。当处理完一个动作,worker进程立即去处理其他游戏中等待处理的动作,或是迎接新玩家的到来。
为什么这比阻塞的、多进程架构更快?
NGINX的可伸缩性非常好,每个worker进程可以支撑成千上万个连接。每个新的连接会创建一个文件描述符以及消耗worker进程中少量的额外内存。每个连接的额外开销极少。NGINX进程可以绑定到CPU。上下文切换是比较罕见的,只有没有任务要处理时才会发生。
在阻塞的、每个连接一个进程的方式下,每个连接都需要大量的额外资源与开销,且上下文切换(从一个进程切换到另一个)非常频繁。
更多细节解释,看看这篇有关NGINX架构的文章。
通过适当的系统调优,NGINX worker进程可以处理成千上万的并发HTTP连接,能够承受流量峰值(新游戏蜂拥而至)还不会错过一个请求。
配置更新与NGINX升级
NGINX这种使用少数worker进程的进程架构,可以非常高效的进行配置更新甚至是更新NGINX介质本身。

更新NGINX配置是个很简单、轻量级且可靠的操作。通常只是意味着去运行一下nginx –s reload命令,这个命令会去检查磁盘上的配置,给主进程发送一个SIGHUP信号。
当主进程收到SIGHUP信号,会做两件事:
- 重新加载配置,然后fork出一组新的worker进程。这些新的worker进程会立马开始接受连接并处理(用的是新的配置)。
- 发信号让旧的worker进程优雅退出。旧的worker进程停止接受新的连接。一旦每个当前的HTTP请求完成,worker进程将利索地结束连接(也就是,没有lingering keeplive)。一旦所有连接都关闭了,worker进程就可以退出了。
这个配置加载进程会造成CPU和内存使用上的一个小峰值,但相比活动的连接带来的资源负载,这是极其微小的。可以每秒重新加载配置多次(有许多NGINX用户的确是这么干的)。多代NGINX worker进程都在等待连接关闭极少会造成问题,但即便有问题也会很快解决。
NGINX介质升级过程达到了高可用性的标准 —— 可以直接对线上运行的NGINX升级,而不会丢失任何连接,也不会有停机时间与服务中断。

介质升级过程与重新加载配置的过程类似。一个新的NGINX主进程会与旧的主进程并行运行,它们共享监听套接字。两个进程都是活动的,并且各自对应的worker进程都还在处理请求。随后可以发信号让旧的主进程及其worker进程优雅退出。
整个过程在Controlling NGINX中有更详细的描述。
总结
这篇深入NGINX信息图从高层次概述了NGINX的功能,但是在这个简单解释的背后,是十多年的创新与优化,才使NGINX在保证安全和可靠性的同时,在众多硬件上都能发挥出最佳性能。
如果想更多地了解NGINX的优化,下面这些资源不错:
- Installing and Tuning NGINX for Performance (webinar; slides at Speaker Deck)
- Tuning NGINX for Performance
- The Architecture of Open Source Applications – NGINX
- Socket Sharding in NGINX Release 1.9.1 (using the SO_REUSEPORT socket option)
原创文章,转载请注明: 转载自并发编程网 – ifeve.com本文链接地址: [译]深入 NGINX: 为性能和扩展所做之设计
[译]深入 NGINX: 为性能和扩展所做之设计的更多相关文章
- Nginx的性能优化方案
nginx的优化 . gzip压缩优化 . expires缓存有还 . 网络IO事件模型优化 . 隐藏软件名称和版本号 . 防盗链优化 . 禁止恶意域名解析 . 禁止通过IP地址访问网站 . HTTP ...
- Nginx服务器性能优化与安全配置实践指南
转载自:https://www.bilibili.com/read/cv16151784?spm_id_from=333.999.0.0 1.引言 1.1 目的 为了更好的指导部署与测试艺术升系统ng ...
- 「译」JUnit 5 系列:扩展模型(Extension Model)
原文地址:http://blog.codefx.org/design/architecture/junit-5-extension-model/ 原文日期:11, Apr, 2016 译文首发:Lin ...
- Nginx配置性能优化
大多数的Nginx安装指南告诉你如下基础知识--通过apt-get安装,修改这里或那里的几行配置,好了,你已经有了一个Web服务器了.而且,在大多数情况下,一个常规安装的nginx对你的网站来说已经能 ...
- Nginx 服务器性能参数设置
Nginx服务器性能调优 Nginx 配置文件 1.根据CPU内核数设置worker进程个数,以12核CPU为例,设置11个worker进程: worker_processes 11; worker_ ...
- 【转】利用TCMalloc优化Nginx的性能
From: http://www.linuxidc.com/Linux/2013-04/83197.html TCMalloc的全称是 Thread-Caching Malloc,是谷歌开发的开源工具 ...
- Nginx配置性能优化(转)
大多数的Nginx安装指南告诉你如下基础知识——通过apt-get安装,修改这里或那里的几行配置,好了,你已经有了一个Web服务器了.而且,在大多数情况下,一个常规安装的nginx对你的网站来说已经能 ...
- 为什么Nginx的性能要比Apache高很多?
为什么Nginx的性能要比Apache高很多? 这得益于Nginx使用了最新的epoll(Linux 2.6内核)和kqueue(freebsd)网络I/O模型,而Apache则使用的是传统的sele ...
- Nginx配置性能优化与压力测试webbench【转】
这一篇我们来说Nginx配置性能优化与压力测试webbench. 基本的 (优化过的)配置 我们将修改的唯一文件是nginx.conf,其中包含Nginx不同模块的所有设置.你应该能够在服务器的/et ...
随机推荐
- 二分答案 + multiset || NOIP 2018 D1 T3 || Luogu P5021 赛道修建
题面:P5021 赛道修建 题解:二分答案,用Dfs进行判断,multiset维护. Dfs(x,fa,Lim)用来计算以x为根的子树中有多少符合条件的路径,并返回剩余未使用的最长路径长. 贪心思想很 ...
- CentOS 6的系统启动流程
一.POST加电自检 按下电源后ROM芯片中的CMOS程序执行并检测CPU.内存等设备是否存在并正常运行,CMOS中的程序叫BIOS,可以设置硬盘接口,网卡声卡开关之类的简单设置.一般PC机主板上有一 ...
- IT项目开发流程
项目开发流程: 一.需求分析:相关系统分析员向用户初步了解需求,然后用相关的工具软件列出要开发的系统的大功能模块,每个大功能模块有哪些小功能模块,对于有些需求比较明确相关的界面时,在这一步里面可以初步 ...
- nginx第三天
nginx架构分析 nginx模块化 nginx基于模块设计,每个模块是一个功能实现,分布式开发,团队协作 核心模块,标准http模块,可选http模块,邮件模块,第三方模块 编译后的源码目录 ob ...
- 安装Go语言及环境的搭建
下载 下载地址 Go官网下载地址:https://golang.org/dl/ Go官方镜像站(推荐):https://golang.google.cn/dl/ 安装 Windows安装 此安装实例以 ...
- element-ui 弹出添加拖拽功能
1.新建dialog.js文件2.在main.js 中引入dialog.js import ‘./utils/dialog.js’3. 使用:<el-dialog v-dialogDrag&g ...
- 自动化登录QQ脚本
1.准备第三方包: py -3.6 -m pip install win32gui py -3.6 -m pip install Pywin32 py -3.6 -m pip install pyHo ...
- JavaScript 运算符是什么?
㈠JavaScript 运算符 ⑴运算符 = 用于赋值. ⑵运算符 + 用于加值. ⑶示例: 向变量赋值,并把它们相加: ; // 向 x 赋值 5 ; // 向 y 赋值 2 var z = ...
- layer 回调
目前使用的layer版本为1.8.5 在调用layer的JS中,使用end来进行处理 $.layer({ type : 2, shadeClose : true, title : '选择人员', cl ...
- codevs 1231 最优布线问题 x(find函数要从娃娃抓起系列)
题目描述 Description 学校需要将n台计算机连接起来,不同的2台计算机之间的连接费用可能是不同的.为了节省费用,我们考虑采用间接数据传输结束,就是一 ...