Nginx架构珠玑

Nginx是由俄罗斯软件工程师Igor Sysoev编写的免费开源Web服务器。自2004年公开发布以来,Nginx一直专注于高性能、高并发性和低内存占用。

而Web服务器功能之上的其他功能,如负载均衡、缓存、访问和带宽控制,以及与各种应用程序高效集成的能力,在nginx上都有体现,从而使nginx成为现代网站架构优选方案。目前,nginx是互联网上第二大受欢迎的开源Web服务器。

Nginx架构概述

传统连接的弊端

传统处理并发连接的方式基于进程或线程的模型,使用单独的进程或线程处理每个连接,以阻塞的方式进行网络或I/O操作。内存和CPU利率性会非常低。

生成单独的进程或线程,需要准备新的运行时环境,包括分配堆和堆栈内存,以及创建新的执行上下文。创建这些项目还需要额外的CPU时间,最终有可能因为过多的上下文切换,导致线程抖动造成性能不佳。

所有这些复杂性,在Apache这样的老式Web服务器架构中都有体现。这是在提供丰富的通用性功能,和优化服务器资源利用率之间的权衡。

Nginx的解决方案

nginx从一开始就以更高的性能、更高效的资源利用率为目标,同时为实现网站的动态增长而生的专业工具,因此它采用了与传统方式不同的模式。 实际上,它受到各种操作系统中,基于事件机制持续开发的影响。以模块化、事件驱动、异步、单线程、非阻塞架构等方案,构成了Nginx代码的基础。

Nginx大量使用多路复用和事件通知,并将特定任务用单独的进程处理。nginx中有一组被称为workers的,数量有限的进程,它们各自以单线程的方式高效循环的处理连接请求。nginx中的每个worker线程每秒可以处理数千个并发连接请求。

Nginx代码结构

Nginx worker代码由核心和功能模块构成。核心负责维护一个密集的消息循环(a tight  run-loop),在请求处理的各个阶段执行对应的逻辑代码块。

功能模块大部分由表示层和应用层功能构成,模块从网络和存储设备中读取内容,并往其中写入内容,转换内容、执行出站过滤,把请求传递给后端服务器。

nginx的模块化架构通常允许开发人员扩展Web服务器功能集,而无需修改核心。nginx模块的各版本略有不同,即核心模块、事件模块、阶段处理程序、协议、变量处理程序、过滤器、上游(接入后端服务)和负载均衡器。

目前,nginx不支持动态的加载模块;即,模块必须在构建阶段与核心一起编译。但是,计划在未来的主版本中支持可加载模块和ABI。有关不同模块角色的更多详细信息后面会提到。

在处理、接受请求、管理网络连接和内容检索时,nginx使用事件通知机制和Linux、Solaris、BSD-based操作系统中的许多磁盘I/O优化技术。

像kqueue、epoll、 event ports。目标是为操作系统提供尽可能多的提示(Hints),以便为入站和出站流量、磁盘操作、读取或写入socket,超时等操作获得及时的异步反馈。

nginx针对基于unix操作系统的多路复用,和高级I/O操作使用了大量不同的优化方法。

Nginx架构抽象概述

Nginx Architecture

 

work模型

如前所述,nginx不会为每个连接生成进程或线程。相反,worker进程接受来自共享“侦听”的socket请求,并执行高效的消息循环(run-loop),每个worker进程处理数千个连接。

在nginx中连接的分配不由nginx决定,这项工作是由操作系统内核负责完成的。Nginx启动时会创建一组初始的socket侦听。然后由worker处理HTTP请求和响应,对socket进行接受、读取、写入。

消息循环(run-loop)是nginx worker代码中最复杂的部分。它包括全面的内部调用,并且在很大程度上依赖于异步任务处理。

异步操作通过对模块化、事件通知、回调函数的广泛使用,和微调定时器( fine-tuned timers)来实现。总体来说,一条关键原则就是尽可能的不阻塞。nginx唯一会阻塞的情况是,当worker进程没有足够的磁盘存储可供执行时。

因为nginx不会为每个连接单开进程或线程,所以在绝大多数情况下,对内存的消耗非常的保守而且非常高效。

与此同时nginx也节省了CPU的周期,因为进程或线程没有在反复的创建 - 销毁模式下运行。nginx所要做的是检查网络和存储的状态、初始化新连接、将它们添加到运行的循环中,并异步处理直到完成,最后连接被解除分配,并从消息循环(run-loop)中删除。

结合使用syscalls和精确实现,如:内存池和内核内存分配器(pool and slab memory allocators),即使在极端工作负载下,nginx通常也可实现中到低的CPU使用率。

因为nginx创建多个workers来处理连接,所以它可以在多核上很好地扩展。通常,worker单独运行在每个核心上,以充分利用多核架构,并防止线程抖动和锁定。没有资源匮乏,资源控制机制在单线程worker进程中被隔离。

此模型还允许跨物理存储设备实现更高的可伸缩性,有助于提高磁盘利用率,并避免阻塞磁盘I/O。因此,在多个worker共享的工作负载下,可以更有效地利用服务器资源。

对于某些磁盘的使用和CPU负载模式,应调整nginx的worker进程的数量 。这里列出了一些基础配置规则,管理员应为Nginx的工作负载多尝试几种配置。

Nginx worker配置建议

如果负载模式是CPU密集型,如:处理大量TCP/IP、执行SSL或压缩 ,此时,nginx worker的数量应与CPU核心数相匹配。

如果负载主要是磁盘I/O,如:从存储器提供不同的内容集、或者重度代理,此时,nginx workers的数量可能是核心数量的1.5倍到2倍。

一些工程师选择worker的依据,是根据存储单元的数量,但这种方法的效率取决于磁盘存储类型和配置。

Nginx的开发人员将在即将推出的版本中,解决的一个主要问题是如何避免磁盘I/O上的大部分阻塞。

如果,目前没有可以满足的存储性能的磁盘操作用于worker,那么,worker从磁盘读取/写入仍是阻塞的。有许多机制和配置文件指令,可以减轻此类磁盘I/O阻塞。

最值得注意的是,sendfile和AIO等选项的组合,通常会为磁盘性能带来很大的空间。应根据数据集、nginx可用的内存量,以及底层存储架构来规划nginx安装项。

现有worker模型与嵌入式脚本的结合是很大的问题(现今已支持lua脚本)。首先,使用标准的nginx发行版,只支持嵌入Perl脚本。

对此有一个简单的说法:问题的关键是嵌入式脚本可能会阻止操作或引发意外退出。这两种行为都会导致worker被立即挂起,从而对一个worker上的数千个连接产生影响。

nginx正准备为这方面工作投入更多,以便使嵌入式脚本更简单、更可靠,适用于更广泛的应用程序。

为什么高并发性很重要?

如今,无处不在的互联网,是十年前的我们难以想象的,最早从简单的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仍然是移动通信的有效速度。

虽然通过增加操作系统内核socket缓冲区的大小,在某种程度上可以改善向慢速客户端发送内容的情况,但这不是解决该问题的通用方法,并且可能具有不良的副作用。

对于持久连接,处理并发性的问题更加明显,为了避免建立新HTTP连接而产生的延迟,客户端将一直保持连接,并且对于每个客户端的连接,Web服务器都需要分配一定的内存。

因此,为了应对持续增长的用户量带来的高并发问题, 网站应该基于许多非常有效的构建块。虽然硬件(CPU,内存,磁盘)、网络带宽、应用程序和数据存储架构等方面很重要,而Web服务器软件用于接受和处理客户端连接,应该能够随着每秒同时连接和请求数量的增加进行非线性地扩展。

Apache不适合吗?

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有更多优势吗?

以高性能和高效率处理高并发性,始终是使用nginx的关键优势。但是,现在有更多有趣的功能。

在过去几年中,Web架构师已经接受了将其应用程序基础结构,与Web服务器分离的想法。然而,以前以LAMP(Linux,Apache,MySQL,PHP,Python或Perl)为基础的网站,现在可能不仅仅是基于LEMP(E'代表'nginx/engine x')。但是,越来越多的做法是将Web服务器推向基础设施的边缘,并以不同的方式围绕它,集成一组应用程序和数据库工具。

Nginx非常适合这一点,因为它提供了许多的关键功能,如:offload并发(将原本在协议栈中进行的IP分片、TCP分段、重组、checksum校验等操作,转移到网卡硬件中进行。降低系统CPU的消耗,提高处理性能),延迟处理,SSL(安全socket层),静态内容,压缩和缓存,连接和请求限制,甚至是从应用程序层,到边缘Web服务器层的HTTP媒体流,Nginx还允许直接与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环境,但Windows版本的nginx更像是概念版,其功能并不齐全,nginx与Windows内核架构存在某些限制,目前这些架构不能很好地互动。

Windows版本的nginx已知问题包括:并发连接数量少、性能低、没有缓存,并且没有带宽监管。Nginx的Windows未来版本将更接近主流功能。