《开源软件架构》--nginx架构概述

640?wx_fmt=gif

14.2. nginx架构概述

传统的基于进程或线程的并发连接处理模型涉及到使用单独的进程或线程处理每个连接,以及阻塞网络或输入/输出操作。根据应用程序的不同,它在利用内存和CPU消耗方面可能非常低效。生成单独的进程或线程需要准备新的运行时环境,包括分配堆和堆栈内存,以及创建新的执行上下文。创建这些还会花费额外的CPU时间,这最终会导致性能低下,因为线程会过多的切换上下文。所有这些复杂性都表现在Apache等较老的web服务器体系结构中。这是在提供丰富的通用特性和优化服务器资源使用之间的权衡。

从一开始,nginx就被认为是一种专业的工具,可以在实现网站动态增长的同时,获得更高的性能、更密集和更经济的服务器资源使用,所以它采用了一种不同的模式。它实际上是受到了各种操作系统中正在开发的基于事件的高级机制的启发。其结果是一个模块化的、事件驱动的、异步的、单线程的、非阻塞的体系结构,它成为nginx代码的基础。

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

  • 代码结构

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

nginx的模块化架构通常允许开发人员在不修改nginx核心的情况下扩展web服务器功能集。nginx模块版本略有不同,即核心模块、事件模块、阶段处理程序、协议、变量处理程序、过滤器、上行流和负载均衡器。此时,nginx不支持动态加载模块;也就是说,模块在构建阶段与核心一起编译。但是,计划在未来的主要版本中支持可加载模块和ABI。

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

nginx架构的高级概述如图14.1所示。

640?wx_fmt=png

Figure 14.1: Diagram of nginx’s architecture

  • Workers 模型

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

运行循环是nginx worker代码中最复杂的部分。它包括全面的内部调用,并严重依赖异步任务处理的思想。异步操作通过模块化、事件通知、广泛使用回调函数和微调计时器来实现。总的来说,关键的原则是尽可能不阻塞。nginx阻塞的惟一情况是worker进程没有足够的磁盘存储性能。

因为nginx不为每个连接派生新的进程或线程,所以内存使用非常有限,在绝大多数情况下非常高效。nginx还节省了CPU周期,因为没有进程或线程的持续创建-销毁模式。nginx所做的是检查网络和存储的状态,初始化新连接,将它们添加到运行循环中,并异步处理,直到完成,此时连接将被释放并从运行循环中删除。结合系统调用的谨慎使用和池和板式内存分配器等支持接口的精确实现,nginx通常可以在极端工作负载下达到中等到低的CPU使用率。

因为nginx生成几个worker来处理连接,所以它可以很好地多核扩展。通常,每个内核使用一个单独的worker能充分利用多核架构,并防止线程抖动和锁定。不存在资源匮乏,资源控制机制被隔离在单线程worker进程中。该模型还允许跨物理存储设备实现更好的可伸缩性,促进更多的磁盘利用率,并避免磁盘I/O上的阻塞。因此,服务器资源在多个worker进程之间共享工作负载的情况下得到了更有效的利用。

对于一些磁盘使用和CPU负载模式,应该调整nginx worker进程的数量。这里的规则有些基础,规则很基础,系统管理员应该针对不同的工作负载尝试一些配置。一般建议如下:如果负载模式是CPU密集型的(例如,处理大量TCP/IP、执行SSL或压缩),那么nginx worker进程的数量应该与CPU内核的数量相匹配;如果负载主要是磁盘I/O限制,(例如,服务于来自存储的不同内容集,或者大量的代理),那么worker的数量可能是CPU内核数量的1.5到2倍。一些工程师根据独立存储单元的数量来选择worker的数量,但是这种方法的效率取决于磁盘存储的类型和配置。

nginx的开发人员将在即将发布的版本中需要解决的一个主要问题是如何避免磁盘I/O上的大部分阻塞。目前,如果没有足够的存储性能来服务于特定worker产生的磁盘操作,该worker可能仍然会阻塞磁盘的读写。存在许多机制和配置文件指令来减少这种磁盘I/O阻塞场景。最值得注意的是,sendfile和AIO等选项的组合通常会为磁盘性能带来很大的空间。nginx的安装应该基于数据集、nginx可用的内存数量和底层存储架构来计划。

现有worker模型的另一个问题与对嵌入式脚本的支持有限。举个例子,对于标准的nginx发行版,只支持嵌入Perl脚本。对此有一个简单的解释:关键问题是嵌入式脚本可能阻塞任何操作或意外退出。这两种行为都会立即导致挂起worker,同时影响数千个连接。计划进行更多的工作,使使用nginx的嵌入式脚本更简单、更可靠,并适用于更广泛的应用程序。

  • nginx流程角色

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

主流程负责以下任务:

  1. 读取和验证配置

  2. 创建、绑定和关闭套接字

  3. 启动、终止和维护配置的worker进程数量

  4. 不中断服务的重新配置

  5. 控制不间断的二进制升级(启动新的二进制并在必要时回滚)

  6. 重启日志文件

  7. 编译嵌入式Perl脚本

worker进程接受、处理和处理来自客户端的连接,提供反向代理和过滤功能,并完成nginx能够完成的几乎所有其他工作。关于监视nginx实例的行为,系统管理员应该关注worker,因为它们是反映web服务器实际日常操作的进程。

缓存加载进程负责检查磁盘上的缓存项,并用缓存元数据填充nginx的内存数据库。本质上,缓存加载进程准备nginx实例来处理已经存储在磁盘上的文件,这些文件位于一个特殊分配的目录结构中。它遍历目录,检查缓存内容元数据,更新共享内存中的相关条目,然后在一切就绪并准备使用时退出。

缓存管理器主要负责缓存过期和失效。在正常的nginx操作过程中,它会保留在内存中,在出现故障时,由主进程重新启动。

  • nginx缓存简要概述

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

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

(全文完)

点击下方

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值