nginx(发音为“engine x”)是由俄罗斯软件工程师Igor Sysoev编写的免费开源Web服务器。自2004年公开发布以来,nginx一直专注于高性能,高并发性和低内存使用。 Web服务器功能之上的其他功能,如负载平衡,缓存,访问和带宽控制,以及与各种应用程序高效集成的能力,有助于使nginx成为现代网站架构的良好选择。目前,nginx是互联网上第二大最受欢迎的开源Web服务器。
如今,互联网如此广泛和无处不在,很难想象它就像十年前我们所知道的那样。从简单的HTML生成可点击文本,基于NCSA,然后是Apache Web服务器,到全球超过20亿用户使用的永远在线的通信媒体,它已经有了很大的发展。随着永久连接的PC,移动设备和最近的平板电脑的激增,互联网格局正在迅速变化,整个经济已经成为数字连线。在线服务变得更加精细,明显偏向即时可用的实时信息和娱乐。运行在线业务的安全方面也发生了重大变化。因此,网站现在比以前复杂得多,并且通常需要更多的工程努力才能具有健壮性和可扩展性。
网站架构师面临的最大挑战之一就是并发。自Web服务开始以来,并发性水平一直在不断增长。一个流行的网站为数十万甚至数百万同时用户提供服务的情况并不少见。十年前,并发的主要原因是缓慢的客户端 - 具有ADSL或拨号连接的用户。如今,并发性是由移动客户端和较新的应用程序体系结构的组合引起的,这些体系结构通常基于维护持久连接,该连接允许客户端使用新闻,推文,朋友订阅源等进行更新。另一个有助于提高并发性的重要因素是现代浏览器的行为改变,它可以打开四到六个同时连接到网站以提高页面加载速度。
为了说明慢客户端的问题,想象一个简单的基于Apache的Web服务器,它产生一个相对较短的100 KB响应 - 一个带有文本或图像的网页。生成或检索此页面只需几分之一秒,但将其传输到带宽为80 kbps(10 KB / s)的客户端需要10秒钟。从本质上讲,Web服务器会相对快速地提取100 KB的内容,然后在释放连接之前,它会忙着将这些内容缓慢地发送到客户端10秒钟。现在假设您有1,000个同时连接的客户,他们请求了类似的内容。如果每个客户端仅分配1 MB的额外内存,则会产生1000 MB(约1 GB)的额外内存,专门用于为1000个客户端提供100 KB的内容。实际上,基于Apache的典型Web服务器通常为每个连接分配超过1 MB的额外内存,令人遗憾的是,几十kbps仍然是移动通信的有效速度。虽然通过增加操作系统内核套接字缓冲区的大小,在某种程度上可以改善向慢速客户端发送内容的情况,但这不是解决该问题的一般方法,并且可能具有不良副作用。
对于持久连接,处理并发性的问题更加明显,因为为了避免与建立新HTTP连接相关联的延迟,客户端将保持连接,并且对于每个连接的客户端,Web服务器分配了一定量的内存。
因此,为了处理与增长的受众相关的增加的工作量以及因此更高的并发性 - 并且能够持续这样做 - 网站应该基于许多非常有效的构建块。虽然硬件(CPU,内存,磁盘),网络容量,应用程序和数据存储架构等方程式的其他部分显然很重要,但是在Web服务器软件中,客户端连接被接受和处理。因此,Web服务器应该能够随着每秒同时连接和请求数量的增加而非线性地扩展
Apache,这种网络服务器软件在很大程度上仍然主宰着互联网,它的根源在于20世纪90年代初。最初,它的架构与当时存在的操作系统和硬件相匹配,但也与互联网状态相匹配,其中网站通常是运行单个Apache实例的独立物理服务器。到了2000年代初,很明显,无法轻松复制独立的Web服务器模型以满足不断增长的Web服务的需求。尽管Apache为未来的开发提供了坚实的基础,但它的架构是为每个新连接生成自己的副本,这不适合网站的非线性可伸缩性。最终,Apache成为了一个通用的Web服务器,专注于拥有许多不同的功能,各种第三方扩展,以及几乎任何类型的Web应用程序开发的普遍适用性。然而,没有任何代价可以实现,并且在单个软件中拥有如此丰富且通用的工具组合的缺点是可扩展性较低,因为每个连接的CPU和内存使用量增加。
因此,当服务器硬件,操作系统和网络资源不再是网站增长的主要限制时,全球的Web开发人员开始寻找更有效的运行Web服务器的方法。大约十年前,着名软件工程师丹尼尔凯格尔宣称“现在是网络服务器同时处理一万个客户的时候了”,并预测了我们现在称之为互联网云服务的东西。 Kegel的C10K清单激发了许多尝试来解决Web服务器优化问题,同时处理大量客户端,nginx被证明是最成功的客户之一。
旨在解决10,000个同时连接的C10K问题,nginx在编写时考虑了不同的体系结构 - 一个更适合同时连接数和每秒请求数的非线性可伸缩性。 nginx是基于事件的,因此它不遵循Apache为每个网页请求生成新进程或线程的风格。最终结果是,即使负载增加,内存和CPU使用率仍然可以管理。 nginx现在可以在具有典型硬件的服务器上提供数万个并发连接。
当nginx的第一个版本发布时,它意味着与Apache一起部署,以便nginx处理静态内容,如HTML,CSS,JavaScript和图像,以卸载基于Apache的应用程序服务器的并发和延迟处理。在开发过程中,nginx通过使用FastCGI,uswgi或SCGI协议以及分布式内存对象缓存系统(如memcached)增加了与应用程序的集成。还添加了其他有用的功能,例如具有负载平衡和缓存的反向代理。这些附加功能使nginx成为有效的工具组合,可构建可扩展的Web基础架构。
2012年2月,Apache 2.4.x分支机构向公众发布。尽管Apache的最新版本增加了新的多处理核心模块和旨在增强可扩展性和性能的新代理模块,但现在判断它的性能,并发性和资源利用率是否与纯事件相当或更好还为时尚早。驱动的Web服务器。不过,看看Apache应用程序服务器在新版本中的扩展性能会非常好,因为它可能会缓解后端方面的瓶颈,这在典型的nginx-plus-Apache Web配置中仍然没有得到解决。
以高性能和高效率处理高并发性始终是部署nginx的关键优势。但是,现在有更多有趣的好处。
在过去几年中,Web架构师已经接受了将其应用程序基础结构与Web服务器分离和分离的想法。然而,以前以LAMP(Linux,Apache,MySQL,PHP,Python或Perl)为基础的网站形式,现在可能不仅仅是基于LEMP的(E'代表'引擎x')但是,越来越多的做法是将Web服务器推向基础设施的边缘,并以不同的方式围绕它集成相同或经过改进的一组应用程序和数据库工具。
nginx非常适合这一点,因为它提供了方便卸载并发,延迟处理,SSL(安全套接字层),静态内容,压缩和缓存,连接和请求限制,甚至来自应用程序的HTTP媒体流所需的关键功能层到更有效的边缘Web服务器层。它还允许直接与memcached / Redis或其他“NoSQL”解决方案集成,以在为大量并发用户提供服务时提高性能。
随着最近开发套件和编程语言的广泛使用,越来越多的公司正在改变他们的应用程序开发和部署习惯。 nginx已成为这些不断变化的范例中最重要的组成部分之一,它已经帮助许多公司在预算范围内快速启动和开发其Web服务。
nginx的第一行是在2002年编写的。2004年,它根据双条款BSD许可证向公众发布。从那以后,nginx用户的数量一直在增长,提供想法,并提交对整个社区非常有帮助和有益的错误报告,建议和观察。
nginx代码库是原创的,完全是用C编程语言从头开始编写的。 nginx已被移植到许多架构和操作系统,包括Linux,FreeBSD,Solaris,Mac OS X,AIX和Microsoft Windows。 nginx有自己的库,除了zlib,PCRE和OpenSSL之外,它的标准模块除了系统的C库之外没有太多用处,如果不需要或者由于潜在的许可证冲突,可以选择从构建中排除它们。
关于Windows版nginx的几句话。虽然nginx适用于Windows环境,但nginx的Windows版本更像是概念验证而不是功能齐全的端口。 nginx和Windows内核架构存在某些限制,目前这些架构不能很好地互动。 Windows的nginx版本的已知问题包括并发连接数量少得多,性能下降,没有缓存以及没有带宽监管。面向Windows的nginx的未来版本将更接近主流功能。
处理并发连接的传统基于进程或线程的模型涉及使用单独的进程或线程处理每个连接,以及阻止网络或输入/输出操作。根据应用程序的不同,内存和CPU消耗可能非常低效。生成单独的进程或线程需要准备新的运行时环境,包括分配堆和堆栈内存,以及创建新的执行上下文。创建这些项目还需要额外的CPU时间,这可能最终导致由于过多的上下文切换时线程抖动导致的性能不佳。所有这些复杂性都表现在像Apache这样的老式Web服务器架构中。这是在提供丰富的一般适用功能和优化服务器资源使用之间的权衡。
从一开始,nginx就是一种专业工具,可以实现更高的性能,密度和经济地使用服务器资源,同时实现网站的动态增长,因此它遵循不同的模式。实际上,它受到各种操作系统中基于事件的高级机制的持续开发的启发。结果是模块化,事件驱动,异步,单线程,非阻塞架构,它成为nginx代码的基础。
nginx大量使用多路复用和事件通知,并将特定任务专用于单独的进程。连接在一个名为workers的有限数量的单线程进程中以高效的运行循环进行处理。在每个worker中,nginx每秒可以处理数千个并发连接和请求。
nginx工作器代码包括核心和功能模块。 nginx的核心负责维护一个严格的运行循环,并在每个请求处理阶段执行模块代码的适当部分。模块构成了大部分表示和应用程序层功能。模块读取和写入网络和存储,转换内容,执行出站过滤,应用服务器端包含操作,并在激活代理时将请求传递给上游服务器。
nginx的模块化架构通常允许开发人员扩展Web服务器功能集,而无需修改nginx核心。 nginx模块的版本略有不同,即核心模块,事件模块,阶段处理程序,协议,变量处理程序,过滤器,上游和负载平衡器。目前,nginx不支持动态加载的模块;即,模块在构建阶段与核心一起编译。但是,计划在未来的主要版本中支持可加载模块和ABI。有关不同模块角色的更多详细信息,请参见第14.4节。
在处理与接受,处理和管理网络连接和内容检索相关的各种操作时,nginx在Linux,Solaris和基于BSD的操作系统中使用事件通知机制和许多磁盘I / O性能增强,如kqueue,epoll,和事件端口。目标是尽可能多地提供操作系统的提示,以获得入站和出站流量的及时异步反馈,磁盘操作,读取或写入套接字,超时等。多路复用和高级I / O操作的不同方法的使用针对运行nginx的每个基于Unix的操作系统进行了大量优化。
图14.1给出了nginx架构的高级概述。
图14.1:nginx架构图
如前所述,nginx不会为每个连接生成进程或线程。相反,工作进程接受来自共享“侦听”套接字的新请求,并在每个工作程序内执行高效的运行循环,以处理每个工作程序数千个连接。在nginx中没有专门的仲裁或与工人的连接分配;这项工作是由OS内核机制完成的。启动时,会创建一组初始侦听套接字。然后,工作人员在处理HTTP请求和响应时不断接受,读取和写入套接字。
运行循环是nginx工作器代码中最复杂的部分。它包括全面的内部调用,并且在很大程度上依赖于异步任务处理的想法。异步操作通过模块化,事件通知,回调函数的广泛使用和微调定时器来实现。总的来说,关键原则是尽可能不阻塞。 nginx仍然可以阻止的唯一情况是当工作进程没有足够的磁盘存储性能时。
因为nginx不会为每个连接分叉进程或线程,所以在绝大多数情况下,内存使用非常保守并且非常有效。 nginx也节省了CPU周期,因为进程或线程没有正在进行的创建 - 销毁模式。 nginx做的是检查网络和存储的状态,初始化新连接,将它们添加到运行循环,并异步处理直到完成,此时连接被解除分配并从运行循环中删除。结合仔细使用系统调用和精确实现支持接口(如池和板内存分配器),即使在极端工作负载下,nginx通常也可实现中到低的CPU使用率。
因为nginx会产生几个工作来处理连接,所以它可以跨多个核心很好地扩展。通常,每个核心的单独工作程序允许充分利用多核架构,并防止线程抖动和锁定。没有资源匮乏,资源控制机制在单线程工作进程中是孤立的。此模型还允许跨物理存储设备实现更高的可伸缩性,有助于提高磁盘利用率并避免阻塞磁盘I / O.因此,在多个工作人员共享的工作负载下,可以更有效地利用服务器资源。
对于某些磁盘使用和CPU负载模式,应调整nginx工作器的数量。这里的规则有点基础,系统管理员应该为他们的工作负载尝试几种配置。一般建议可能如下:如果负载模式是CPU密集型 - 例如,处理大量TCP / IP,执行SSL或压缩 - nginx工作器的数量应与CPU核心数相匹配;如果负载主要是磁盘I / O绑定 - 例如,从存储服务不同的内容集,或者重度代理 - 工作者数量可能是核心数量的1.5倍到2倍。一些工程师根据单个存储单元的数量来选择工人数量,但这种方法的效率取决于磁盘存储的类型和配置。
nginx的开发人员将在即将推出的版本中解决的一个主要问题是如何避免磁盘I / O上的大部分阻塞。目前,如果没有足够的存储性能来为特定工作人员生成的磁盘操作提供服务,那么该工作人员仍可能阻止从磁盘读取/写入。存在许多机制和配置文件指令以减轻此类磁盘I / O阻塞方案。最值得注意的是,sendfile和AIO等选项的组合通常会为磁盘性能带来很大的空间。应根据数据集,nginx可用的内存量以及底层存储架构来规划nginx安装。
现有工作模型的另一个问题与嵌入式脚本的有限支持有关。首先,使用标准的nginx发行版,只支持嵌入Perl脚本。有一个简单的解释:关键问题是嵌入式脚本可能阻止任何操作或意外退出。这两种行为都会立即导致工作人员被挂起,同时影响数千个连接。计划进行更多工作,使nginx的嵌入式脚本更简单,更可靠,适用于更广泛的应用程序。
nginx在内存中运行多个进程;有一个主进程和几个工作进程。还有一些特殊用途的进程,特别是缓存加载器和缓存管理器。所有进程都是nginx版本1.x中的单线程。所有进程主要使用共享内存机制进行进程间通信。主进程以root用户身份运行。缓存加载器,缓存管理器和工作程序作为非特权用户运行。
主进程负责以下任务:
工作进程接受,处理和处理来自客户端的连接,提供反向代理和过滤功能,并执行nginx能够执行的几乎所有其他操作。关于监视nginx实例的行为,系统管理员应该关注工作人员,因为他们是反映Web服务器的实际日常操作的过程。
缓存加载器进程负责检查磁盘缓存项并使用缓存元数据填充nginx的内存数据库。本质上,缓存加载器准备nginx实例以处理已经存储在磁盘上的文件,这些文件位于特殊分配的目录结构中。它遍历目录,检查缓存内容元数据,更新共享内存中的相关条目,然后在一切都干净并准备好使用时退出。
缓存管理器主要负责缓存过期和失效。它在正常的nginx操作期间保留在内存中,并在发生故障时由主进程重新启动。
nginx中的缓存是以文件系统上的分层数据存储的形式实现的。缓存键是可配置的,并且可以使用不同的请求特定参数来控制进入缓存的内容。缓存密钥和缓存元数据存储在共享内存段中,缓存加载器,缓存管理器和工作人员可以访问这些内存段。目前,除了操作系统的虚拟文件系统机制所暗示的优化之外,没有任何内存中的文件缓存。每个缓存的响应都放在文件系统上的不同文件中。层次结构(级别和命名细节)通过nginx配置指令控制。将响应写入缓存目录结构时,文件的路径和名称将从代理URL的MD5哈希派生。
将内容放入缓存的过程如下:当nginx从上游服务器读取响应时,首先将内容写入缓存目录结构之外的临时文件。当nginx完成处理请求时,它会重命名临时文件并将其移动到缓存目录。如果代理的临时文件目录位于另一个文件系统上,则会复制该文件,因此建议将临时目录和缓存目录保留在同一文件系统上。当需要明确清除文件时,从缓存目录结构中删除文件也是非常安全的。 nginx有第三方扩展,可以远程控制缓存内容,并计划在主要发行版中集成更多功能。
原文链接