<返回更多

Nginx架构介绍

2021-03-22    
加入收藏

Nginx(发音为“ engine x”)是由俄罗斯软件工程师Igor Sysoev编写的免费开源Web服务器。自2004年公开发布以来,nginx一直致力于高性能,高并发性和低内存使用率。Web服务器功能之上的其他功能,例如负载平衡,缓存,访问和带宽控制,以及与各种应用程序高效集成的能力,已使Nginx成为现代网站体系结构的理想选择。当前,nginx是Internet上第二受欢迎的开源Web服务器。

为什么高并发很重要?

如今,互联网是如此的广泛和无所不在,很难想象十年前它并不完全存在。它已经从基于NCSA的简单html产生可点击文本,然后基于Apache Web服务器发展到了始终在线的通信介质,该通信介质已被全世界超过20亿用户使用。随着永久连接的PC,移动设备和最近的平板电脑的普及,互联网格局正在迅速变化,整个经济体已经实现了数字化连接。在线服务变得更加复杂,并且对即时可用的实时信息和娱乐有明显的偏见。运行在线业务的安全性方面也已发生重大变化。因此,网站现在比以前复杂得多,

并发一直是网站架构师面临的最大挑战之一。自Web服务开始以来,并发水平一直在不断提高。一个受欢迎的网站为成千上万甚至数百万的同时用户提供服务的情况并不少见。十年前,并发的主要原因是客户端速度较慢,即具有ADSL或拨号连接的用户。如今,并发是由移动客户端和较新的应用程序体系结构的结合引起的,这些体系结构通常基于维护持久性连接,该持久性连接允许使用新闻,推文,朋友供稿等更新客户端。导致并发性增加的另一个重要因素是现代浏览器的行为发生了变化,现代浏览器打开了网站的四到六个同时连接,以提高页面加载速度。

为了说明客户端速度慢的问题,请想象一个简单的基于Apache的Web服务器,它会产生相对较短的100 KB响应,即带有文本或图像的Web页面。生成或检索此页面仅需不到一秒钟的时间,但是将其传输到带宽为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不适合吗

Apache,至今仍在互联网上占主导地位的Web服务器软件,其起源可追溯到1990年代初。最初,它的体系结构与当时存在的操作系统和硬件相匹配,但也与Internet的状态相匹配,在Internet上,网站通常是运行单个Apache实例的独立物理服务器。到2000年代初,很明显,无法轻松地复制独立的Web服务器模型来满足不断增长的Web服务的需求。尽管Apache为将来的开发提供了坚实的基础,但它的架构是为每个新连接生成自己的副本,这不适用于网站的非线性可伸缩性。最终,Apache成为了通用Web服务器,致力于提供许多不同的功能,各种第三方扩展,以及对几乎任何类型的Web应用程序开发的普遍适用性。但是,价格不菲,而且在单个软件中拥有如此丰富而通用的工具组合的缺点是可伸缩性较差,这是因为每个连接的CPU和内存使用量增加了。

因此,当服务器硬件,操作系统和网络资源不再是网站增长的主要限制时,全世界的Web开发人员开始四处寻找运行Web服务器的更有效方法。大约十年前,著名的软件工程师Daniel Kegel宣称“现在是Web服务器同时处理一万个客户端的时候了”,并预测了我们现在所说的Internet云服务。凯格尔(Kegel)的C10K清单激发了许多尝试来解决Web服务器优化问题,以同时处理大量客户端,而Nginx证明是最成功的解决方案之一。

为了解决10,000个并发连接的C10K问题,nginx在编写时就考虑了另一种体系结构,该体系结构更适合于同时连接数和每秒请求数方面的非线性可伸缩性。nginx是基于事件的,因此它不遵循Apache的样式来为每个网页请求生成新的进程或线程。最终结果是,即使负载增加,内存和CPU使用率仍可控制。现在,nginx可以在具有典型硬件的服务器上提供数以万计的并发连接。

Nginx的第一个版本发布时,它打算与Apache一起部署,以便Nginx处理诸如HTML,cssJAVAScript和图像之类的静态内容,以减轻基于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,MySQLphpPython或Perl)的网站形式存在的网站现在可能不再仅仅是基于LEMP的网站(“ E”代表“ Engine 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拥有自己的库,并且其标准模块在系统的C库之外使用很少,除了zlib,PCRE和OpenSSL外,如果不需要或由于潜在的许可证冲突,可以选择将其从构建中排除。

关于Windows版本的Nginx的几句话。尽管nginx在Windows环境中工作,但Windows版本的nginx更像是概念验证,而不是功能齐全的端口。Nginx和Windows内核体系结构存在某些局限性,目前无法很好地进行交互。Windows的nginx版本的已知问题包括并发连接数少得多,性能下降,没有缓存和没有带宽策略。适用于Windows的Nginx的未来版本将更接近主流功能。

Nginx架构概述

传统的基于进程或线程的处理并发连接的模型涉及使用单独的进程或线程处理每个连接,并阻塞网络或输入/输出操作。根据应用程序的不同,它在内存和CPU消耗方面可能效率很低。生成单独的进程或线程需要准备新的运行时环境,包括分配堆和堆栈内存以及创建新的执行上下文。创建这些项目也要花费额外的CPU时间,由于过度的上下文切换导致线程崩溃,最终可能导致性能下降。所有这些复杂性都在Apache之类的较旧的Web服务器体系结构中体现出来。这是在提供丰富的一组普遍适用的功能与优化使用服务器资源之间的权衡。

从一开始,nginx就是一种专用工具,可实现更高的性能,密度和服务器资源的经济使用,同时实现网站的动态增长,因此它采用了不同的模型。实际上,它受到了各种操作系统中基于事件的高级机制的持续开发的启发。结果就是模块化,事件驱动,异步,单线程,无阻塞的体系结构,该体系结构成为nginx代码的基础。

nginx大量使用多路复用和事件通知,并将特定任务专用于分离进程。连接在数量有限的称为workers的单线程进程中以高效的运行循环进行处理。每个workernginx内每秒可以处理数千个并发连接和请求。

代码结构

Nginxworker代码包括核心和功能模块。nginx的核心负责维护紧密的运行循环,并在请求处理的每个阶段执行模块代码的适当部分。模块构成了大多数表示层和应用程序层功能。激活代理后,模块可从网络读取和写入网络和存储,转换内容,进行出站筛选,应用服务器端包含操作并将请求传递给上游服务器。

nginx的模块化体系结构通常允许开发人员在不修改nginx核心的情况下扩展Web服务器功能集。nginx模块的形式略有不同,即核心模块,事件模块,阶段处理程序,协议,变量处理程序,过滤器,上游和负载平衡器。目前,nginx不支持动态加载的模块。即,在构建阶段将模块与核心一起进行编译。但是,计划在将来的主要版本中支持可装载模块和ABI。有关不同模块的角色的更多详细信息,请参见第14.4节。

同时处理各种具有接受,处理和管理的网络连接和内容检索,nginx的用途事件通知机制和多个磁盘I /在Linux,Solaris和O性能增强基于BSD操作系统,等等相关联的动作kqueue,epoll和event ports。目标是为操作系统提供尽可能多的提示,以获取针对入站和出站流量,磁盘操作,套接字的读写操作,超时等的及时异步反馈。对于nginx所运行的每个基于Unix的操作系统,都对使用不同方法进行多路复用和高级I / O操作进行了优化。

下图给出了Nginx架构的高级概述。

Nginx架构介绍

Nginx的架构图

Worker模型

如前所述,nginx不会为每个连接生成一个进程或线程。取而代之的是,worker进程接受来自共享“侦听”套接字的新请求,并在每个worker进程内部执行高效的运行循环,以每个进程处理数千个连接worker。worker在nginx中,没有专门的仲裁或分配到s的连接;这项工作是由OS内核机制完成的。启动时,将创建一组初始的侦听套接字。worker然后在处理HTTP请求和响应的同时不断接受,读取和写入套接字。

运行循环是Nginxworker代码中最复杂的部分。它包括全面的内部调用,并且在很大程度上依赖于异步任务处理的思想。异步操作是通过模块化,事件通知,回调函数的广泛使用和经过微调的计时器来实现的。总体而言,关键原则是尽可能不阻塞。nginx仍然可以阻止的唯一情况是,当worker进程的磁盘存储性能不足时。

由于nginx不会为每个连接派生一个进程或线程,因此在大多数情况下,内存使用非常保守且非常高效。nginx还可以节省CPU周期,因为进程或线程没有持续的create-destroy模式。nginx的作用是检查网络和存储的状态,初始化新连接,将它们添加到运行循环中,并异步处理直到完成,这时将连接释放并从运行循环中删除。结合使用syscalls的谨慎使用和对接口(如池和平板内存分配器)的支持的准确实现,即使在极端的工作负载下,nginx通常也可以实现中等到低的CPU使用率。

由于nginx产生几个workers来处理连接,因此它可以在多个内核之间很好地扩展。通常,worker每个内核使用单独的内核可以充分利用多核体系结构,并防止线程崩溃和锁定。没有资源匮乏,并且资源控制机制被隔离在单线程worker进程中。该模型还允许跨物理存储设备更大的可伸缩性,促进更多的磁盘利用率,并避免阻塞磁盘I / O。结果,服务器资源可以更有效地利用,并且在多个工作人员之间共享工作负载。

对于某些磁盘使用情况和CPU负载模式,worker应调整nginx的数量。这些规则在这里有些基本,系统管理员应针对其工作负载尝试一些配置。一般建议如下:如果负载模式是CPU密集型的(例如,处理大量TCP / IP,执行SSL或压缩),则nginxworker的数量应与CPU内核的数量匹配;如果负载主要是受磁盘I / O约束的(例如,从存储服务中提供不同的内容集或进行大量代理),则workers的数量可能是内核数量的一半到两倍。有些工程师worker根据单个存储单元的数量来选择s的数量,尽管这种方法的效率取决于磁盘存储的类型和配置。

Nginx开发人员将在即将发布的版本中解决的一个主要问题是如何避免磁盘I / O的大部分阻塞。目前,如果没有足够的存储性能来满足特定磁盘所生成的磁盘操作worker,则worker可能仍无法从磁盘进行读/写操作。存在许多机制和配置文件指令来减轻此类磁盘I / O阻塞的情况。最值得注意的是,诸如sendfile和AIO之类的选项组合通常会为磁盘性能产生很大的扩展空间。应该基于数据集,可用于nginx的内存量以及基础存储架构来规划nginx的安装。

现有worker模型的另一个问题与对嵌入式脚本的有限支持有关。首先,使用标准的nginx发行版,仅支持嵌入Perl脚本。对此有一个简单的解释:关键问题是嵌入式脚本可能阻塞任何操作或意外退出。两种类型的行为都将立即导致工人被吊死的情况,同时影响成千上万的连接。计划进行更多工作,以使使用nginx的嵌入式脚本更简单,更可靠,并适合更广泛的应用程序。

nginx流程角色

nginx在内存中运行多个进程;有一个主过程和几个worker过程。还有两个特殊目的的过程,特别是缓存加载器和缓存管理器。所有进程在nginx的1.x版本中都是单线程的。所有进程主要使用共享内存机制进行进程间通信。主进程以root用户身份运行。缓存加载器,缓存管理器和worker均以非特权用户身份运行。

主流程负责以下任务:

  • 读取并验证配置
  • 创建,绑定和关闭套接字
  • 启动,终止和维护配置的worker进程数
  • 重新配置而不会中断服务
  • 控制不间断二进制升级(启动新二进制并在必要时回滚)
  • 重新打开日志文件
  • 编译嵌入式Perl脚本

该worker过程接受来自客户端的处理和过程连接,提供反向代理和过滤功能,并做几乎其他一切nginx的是可以胜任的。关于监视nginx实例的行为,系统管理员应密切注意workers,因为它们是反映Web服务器实际日常操作的过程。

缓存加载器进程负责检查磁盘上的缓存项,并使用缓存元数据填充nginx的内存数据库。本质上,缓存加载器准备nginx实例,以与已经存储在磁盘上的文件(以特别分配的目录结构)一起工作。它遍历目录,检查缓存内容元数据,更新共享内存中的相关条目,然后在一切准备就绪且可用时退出。

缓存管理器主要负责缓存的过期和失效。在正常的Nginx操作期间,它会保留在内存中,如果发生故障,它将由主进程重新启动。

Nginx缓存概述

nginx中的缓存以文件系统上分层数据存储的形式实现。高速缓存键是可配置的,并且可以使用不同的特定于请求的参数来控制进入高速缓存的内容。缓存键和缓存元数据存储在共享内存段中,缓存加载器,缓存管理器和worker可以访问这些共享内存段。当前,除了操作系统的虚拟文件系统机制所隐含的优化之外,没有任何文件内的内存缓存。每个缓存的响应都放置在文件系统上的不同文件中。层次结构(级别和命名详细信息)由nginx配置指令控制。将响应写入高速缓存目录结构时,文件的路径和名称是从代理URL的MD5哈希得出的。

将内容放置在缓存中的过程如下:当nginx从上游服务器读取响应时,首先将内容写入到缓存目录结构之外的临时文件中。当nginx完成对请求的处理后,它将重命名临时文件并将其移至缓存目录。如果用于代理的临时文件目录位于另一个文件系统上,则将复制该文件,因此建议将临时目录和缓存目录都保留在同一文件系统上。当需要显式清除文件时,从缓存目录结构中删除文件也是非常安全的。Nginx有第三方扩展,可以远程控制缓存的内容,并且计划将更多功能集成到主发行版中。

nginx配置

Nginx的配置系统受到Igor Sysoev在Apache方面的经验的启发。他的主要见解是,可伸缩的配置系统对于Web服务器至关重要。在维护具有许多虚拟服务器,目录,位置和数据集的大型复杂配置时,遇到了主要的扩展问题。在相对较大的Web设置中,如果在应用程序级别和系统工程师本人均未正确执行的话,这可能是一场噩梦。

因此,nginx配置旨在简化日常操作,并为进一步扩展Web服务器配置提供简便的方法。

nginx配置保存在许多通常位于/usr/local/etc/nginx或中的纯文本文件中/etc/nginx。主要配置文件通常称为nginx.conf。为了使它整洁,可以将部分配置放在单独的文件中,这些文件可以自动包含在主文件中。但是,此处应注意,nginx当前不支持Apache样式的分布式配置(即.htaccess文件)。与nginx Web服务器行为有关的所有配置都应驻留在一组集中的配置文件中。

最初由主进程读取并验证配置文件。worker从主进程派生出来的进程可以使用nginx配置的已编译只读形式。配置结构由通常的虚拟内存管理机制自动共享。

nginx的配置有几种不同的情况下main,http,server,upstream,location(也mail指示为邮件代理)块。上下文永远不会重叠。例如,没有将location块放在main指令块中的事情。另外,为避免不必要的歧义,没有类似“全局Web服务器”的配置。nginx配置的目的是干净逻辑,允许用户维护包含数千个指令的复杂配置文件。Sysoev在一次私下交谈中说:“全局服务器配置中的位置,目录和其他块是我在Apache中从未喜欢的功能,因此这就是为什么它们从未在nginx中实现的原因。”

配置语法,格式和定义遵循所谓的C样式约定。各种各样的开放源代码和商业软件应用程序已经使用了这种制作配置文件的特殊方法。通过设计,C风格的配置非常适合嵌套的描述,逻辑性强,易于创建,阅读和维护,并且受到许多工程师的喜爱。Nginx的C样式配置也可以轻松实现自动化。

尽管某些nginx指令类似于Apache配置的某些部分,但是设置nginx实例却是完全不同的体验。例如,nginx支持重写规则,尽管它需要管理员手动调整旧版Apache重写配置以匹配nginx样式。重写引擎的实现也有所不同。

通常,nginx设置还支持多种原始机制,这些机制作为精益Web服务器配置的一部分非常有用。简要提及变量和try_files指令是有意义的,这在nginx中是唯一的。开发nginx中的变量是为了提供附加的甚至更强大的机制来控制Web服务器的运行时配置。优化了变量以进行快速评估,并在内部将其预编译为索引。评估是按需完成的;也就是说,变量的值通常只计算一次,并在特定请求的生存期内进行缓存。变量可以与不同的配置指令一起使用,从而为描述条件请求处理行为提供了额外的灵活性。

该try_files指令最初旨在以if一种更适当的方式逐渐替换条件配置语句,并且该指令旨在快速有效地尝试/匹配不同的URI到内容的映射。总体而言,该try_files指令运行良好,并且可能非常有效和有用。建议读者彻底检查该try_files指令,并在适用的情况下采用该指令。

nginx内部

如前所述,nginx代码库由一个核心和许多模块组成。Nginx的核心负责提供Web服务器,Web和邮件反向代理功能的基础;它启用了底层网络协议的使用,构建了必要的运行时环境,并确保了不同模块之间的无缝交互。但是,大多数特定于协议和应用程序的功能是由nginx模块而不是核心完成的。

在内部,nginx通过模块的管道或链来处理连接。换句话说,对于每个操作,都有一个模块在做相关的工作。例如,压缩,修改内容,执行服务器端包含,通过FastCGI或uwsgi协议与上游应用程序服务器进行通信,或与memcached进行通信。

在内核和真正的“功能”模块之间的某个位置有几个nginx模块。这些模块是http和mail。这两个模块在核心组件和较低级别的组件之间提供了附加的抽象级别。在这些模块中,实现了与相应的应用程序层协议(如HTTP,SMTP或IMAP)关联的事件序列的处理。这些上层模块与nginx核心结合,负责维护对各个功能模块的正确调用顺序。目前,HTTP协议已作为该http模块的一部分实现,但由于需要支持SPDY等其他协议,因此计划在将来将其分成功能模块。SPDY:一种用于更快Web的实验性协议”)。

功能模块可分为事件模块,阶段处理程序,输出过滤器,变量处理程序,协议,上游和负载平衡器。尽管事件模块和协议也用于,但大多数这些模块都是nginx的HTTP功能的补充mail。事件模块提供了特定于OS的事件通知机制,例如kqueue或epoll。Nginx使用的事件模块取决于操作系统功能和构建配置。协议模块允许nginx通过HTTPS,TLS / SSL,SMTP,POP3和IMAP进行通信。

一个典型的HTTP请求处理周期如下所示。

  1. 客户端发送HTTP请求。
  2. nginx核心根据与请求匹配的已配置位置选择适当的阶段处理程序。
  3. 如果配置为这样做,则负载平衡器将选择上游服务器进行代理。
  4. 阶段处理程序执行其工作,并将每个输出缓冲区传递给第一个过滤器。
  5. 第一个过滤器将输出传递到第二个过滤器。
  6. 第二个过滤器将输出传递到第三个(依此类推)。
  7. 最终响应将发送给客户端。

nginx模块的调用是高度可定制的。它通过使用指向可执行函数的指针的一系列回调来执行。但是,这样做的缺点是可能给希望编写自己的模块的程序员带来沉重负担,因为他们必须准确定义模块的运行方式和运行时间。nginx API和开发人员的文档都得到了改进,可以缓解这种情况。

模块可以连接的位置的一些示例是:

  • 在读取和处理配置文件之前
  • 对于该位置及其所在的服务器的每个配置指令
  • 初始化主配置时
  • 服务器(即主机/端口)初始化时
  • 服务器配置与主配置合并时
  • 当配置配置被初始化或与父服务器配置合并时
  • 主进程开始或退出时
  • 当新的工作进程启动或退出时
  • 处理请求时
  • 过滤响应标头和正文时
  • 在选择,启动和重新启动对上游服务器的请求时
  • 处理上游服务器的响应时
  • 完成与上游服务器的交互时

在内worker,导致运行循环(在其中生成响应)的操作序列如下所示:

  1. 开始ngx_worker_process_cycle()。
  2. 使用操作系统特定的机制(例如epoll或kqueue)处理事件。
  3. 接受事件并调度相关动作。
  4. 流程/代理请求标头和正文。
  5. 生成响应内容(标题,正文)并将其流式传输到客户端。
  6. 完成请求。
  7. 重新初始化计时器和事件。

运行循环本身(第5步和第6步)确保增量生成响应并将其流传输到客户端。

处理HTTP请求的更详细的视图可能如下所示:

  1. 初始化请求处理。
  2. 进程头。
  3. 工艺体。
  4. 调用关联的处理程序。
  5. 运行处理阶段。

这使我们进入了阶段。当nginx处理HTTP请求时,它将通过多个处理阶段传递它。在每个阶段都有处理程序要调用。通常,阶段处理程序处理请求并产生相关输出。相位处理程序将附加到配置文件中定义的位置。

阶段处理程序通常做四件事:获取位置配置,生成适当的响应,发送标头和发送正文。处理程序只有一个参数:描述请求的特定结构。请求结构具有许多有关客户端请求的有用信息,例如请求方法,URI和标头。

读取HTTP请求标头后,nginx会查找关联的虚拟服务器配置。如果找到了虚拟服务器,则请求将经历六个阶段:

  1. 服务器重写阶段
  2. 定位阶段
  3. 位置重写阶段(可以将请求带回到上一个阶段)
  4. 访问控制阶段
  5. try_files阶段
  6. 对数阶段

为了响应请求生成必要的内容,nginx将请求传递给合适的内容处理程序。根据确切位置的配置,nginx的可先试用所谓的无条件处理,如perl,proxy_pass,flv,mp4,等。如果请求不通过下面的处理程序之一匹配任何内容处理器之上,这是挑选的,在这个确切顺序为:random index,index,autoindex,gzip_static,static。

可以在Nginx文档中找到索引模块的详细信息,但是这些模块处理带有尾部斜杠的请求。如果像这样的专用模块mp4或autoindex不合适的模块,则将内容视为磁盘上的文件或目录(即静态),并由static内容处理程序提供服务。对于目录,它将自动重写URI,以使结尾的斜杠始终存在(然后发出HTTP重定向)。

然后,内容处理程序的内容将传递到过滤器。过滤器也附加到位置,并且可以为一个位置配置多个过滤器。过滤器执行操作处理程序产生的输出的任务。过滤器的执行顺序在编译时确定。对于开箱即用的过滤器,它是预定义的,对于第三方过滤器,可以在构建阶段进行配置。在现有的nginx实现中,过滤器只能进行出站更改,并且目前没有机制可以编写和附加过滤器来进行输入内容转换。输入过滤将出现在Nginx的未来版本中。

过滤器遵循特定的设计模式。一个过滤器被调用,开始工作,并调用下一个过滤器,直到调用链中的最后一个过滤器为止。之后,nginx完成响应。过滤器不必等待上一个过滤器完成。一旦前一个过滤器的输入可用(功能非常类似于Unix管道),链中的下一个过滤器就可以开始自己的工作。反过来,可以在接收到来自上游服务器的整个响应之前,将生成的输出响应传递给客户端。

有标题过滤器和主体过滤器。nginx将响应的标头和正文分别馈送到关联的过滤器。

标头过滤器包含三个基本步骤:

  1. 决定是否对此响应进行操作。
  2. 根据响应进行操作。
  3. 调用下一个过滤器。

主体过滤器可转换生成的内容。身体过滤器的示例包括:

  • 服务器端包含
  • XSLT过滤
  • 图像过滤(例如,即时调整图像大小)
  • 字符集修改
  • gzip 压缩
  • 分块编码

在过滤器链之后,响应将传递到编写器。除编写器外,还有其他几个特殊用途的过滤器,即copy过滤器和postpone过滤器。该copy过滤器是负责填充内存缓冲区有可能被存储在一个代理临时目录相关的响应内容。该postpone过滤器用于子请求。

子请求是用于请求/响应处理的非常重要的机制。子请求也是Nginx最强大的方面之一。通过子请求,nginx可以从与客户端最初请求的URL不同的URL返回结果。一些Web框架将其称为内部重定向。但是,nginx更进一步-过滤器不仅可以执行多个子请求并将输出组合到单个响应中,而且子请求还可以嵌套和分层。子请求可以执行自己的子子请求,并且子子请求可以发起子子子请求。子请求可以映射到硬盘,其他处理程序或上游服务器上的文件。子请求对于基于原始响应中的数据插入其他内容最有用。例如,include带有指定URL内容的指令。或者,它可能是制作过滤器的示例,该过滤器将文档的全部内容视为要检索的URL,然后将新文档附加到URL本身。

上游和负载均衡器也值得简要描述。上游用于实现可以识别为内容处理程序的内容,该内容处理程序是反向代理(proxy_pass处理程序)。上游模块通常会准备要发送到上游服务器(或“后端”)的请求,并从上游服务器接收响应。这里没有对输出过滤器的调用。上游模块确切执行的操作是在准备好写入和读取上游服务器时调用设置的回调。存在实现以下功能的回调:

  • 制作请求缓冲区(或它们的链)以发送到上游服务器
  • 重新初始化/重置与上游服务器的连接(在再次创建请求之前发生)
  • 处理上游响应的第一位并保存指向从上游服务器接收的有效负载的指针
  • 终止请求(在客户端过早终止时发生)
  • 当Nginx完成从上游服务器的读取时完成请求
  • 整理响应主体(例如,移除拖车)

负载平衡器模块连接到proxy_pass处理程序,以在有多个上游服务器符合条件时提供选择上游服务器的能力。负载平衡器注册一个启用配置文件指令,提供其他上游初始化功能(以解析DNS中的上游名称等),初始化连接结构,决定将请求路由到何处,并更新统计信息。当前,nginx支持两种标准规范来平衡上游服务器的负载:轮询和ip-hash。

上游和负载平衡处理机制包括用于检测故障上游服务器并将新请求重新路由到其余请求的算法,尽管计划进行许多其他工作来增强此功能。通常,计划在负载平衡器上进行更多工作,并且在Nginx的下一版本中,将大大改善跨不同上游服务器分配负载以及运行状况检查的机制。

还有两个其他有趣的模块,它们提供了一组在配置文件中使用的变量。虽然nginx中的变量是在不同的模块之间创建和更新的,但有两个完全专用于变量的模块:geo和map。该geo模块用于根据客户端的IP地址促进对客户端的跟踪。该模块可以创建依赖于客户端IP地址的任意变量。另一个模块map允许从其他变量创建变量,从本质上讲,它提供了对主机名和其他运行时变量进行灵活映射的功能。这种模块可以称为变量处理程序。

在单个nginx内实现的内存分配机制在worker某种程度上受到Apache的启发。Nginx内存管理的高级描述如下:对于每个连接,动态分配,链接必需的内存缓冲区,将其用于存储和处理请求和响应的标头和主体,然后在连接释放时释放它们。请务必注意,nginx会尽量避免在内存中复制数据,并且大多数数据是通过指针值传递的,而不是通过调用传递的memcpy。

更深入一点,当模块生成响应时,将检索到的内容放入内存缓冲区中,然后将其添加到缓冲区链链接中。随后的处理也与此缓冲区链链接一起工作。在Nginx中,缓冲区链非常复杂,因为根据模块类型的不同,有多种处理方案。例如,在实现主体过滤器模块时精确地管理缓冲区可能非常棘手。这样的模块一次只能在一个缓冲区(链链接)上运行,并且必须决定是覆盖输入缓冲区,用新分配的缓冲区替换该缓冲区,还是在相关缓冲区之前或之后插入新缓冲区。使事情复杂化的是,有时模块会接收多个缓冲区,因此它具有不完整的缓冲区链,必须对其进行操作。然而,

关于上述方法的注释是,在连接的整个生命周期中都分配了内存缓冲区,因此对于寿命长的连接,会保留一些额外的内存。同时,在空闲的keepalive连接上,nginx仅花费550字节的内存。对于Nginx的将来版本,可能的优化方法是重用和共享内存缓冲区以用于长期连接。

管理内存分配的任务由Nginx池分配器完成。共享内存区域用于接受互斥,高速缓存元数据,SSL会话高速缓存以及与带宽管制和管理(限制)相关的信息。在nginx中实现了一个平板分配器来管理共享内存分配。为了允许同时安全地使用共享内存,可以使用多种锁定机制(互斥体和信号量)。为了组织复杂的数据结构,nginx还提供了一个红黑树实现。红黑树用于将缓存元数据保留在共享内存中,跟踪非正则表达式位置定义以及执行其他一些任务。

不幸的是,上述所有内容从未以一致和简单的方式进行描述,这使得为nginx开发第三方扩展的工作变得相当复杂。尽管存在一些有关nginx内部的良好文档(例如,由Evan Miller编写的文档),但此类文档仍需要大量的逆向工程工作,并且nginx模块的实现对于许多人来说仍然是妖术。

尽管与第三方模块开发相关的某些困难,但Nginx用户社区最近看到了许多有用的第三方模块。例如,有一个用于nginx的嵌入式Lua解释器模块,用于负载平衡的其他模块,全面的WebDAV支持,高级缓存控制以及本章的作者鼓励并将来会支持的其他有趣的第三方工作。

教训

当Igor Sysoev开始编写nginx时,支持Internet的大多数软件已经存在,并且此类软件的体系结构通常遵循传统服务器和网络硬件,操作系统以及旧的Internet体系结构的定义。但是,这并没有阻止Igor认为他可能能够改进Web服务器领域的功能。因此,尽管第一课似乎很明显,但事实是:总有改进的余地。

考虑到更好的Web软件的想法,Igor花了很多时间来开发初始代码结构,并研究了针对各种操作系统优化代码的不同方法。十年后,考虑到对版本1的积极开发,他正在开发nginx版本2.0的原型。很显然,新架构的初始原型和初始代码结构对于未来的发展至关重要。软件产品。

值得一提的另一点是,应重点发展。Windows版本的nginx可能是一个很好的例子,它说明了如何避免将开发工作浪费在既不是开发人员的核心能力也不是目标应用程序的东西上是值得的。它同样适用于在尝试使用更多功能增强nginx以便与现有旧式设置向后兼容的过程中出现的重写引擎。

最后但并非最不重要的一点是,值得一提的是,尽管Nginx开发人员社区不是很大,但Nginx的第三方模块和扩展一直是其受欢迎程度的重要组成部分。Nginx用户社区及其原始开发人员对Evan Miller,Piotr Sikora,Valery Kholodkov,Zhang Yichun(agentzh)和其他才华横溢的软件工程师所做的工作表示赞赏。

(本文由闻数起舞翻译自Andrew Alexeev的文章《Nginx》,转载请注明出处,原文链接:
https://www.aosabook.org/en/nginx.html)

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>