原文:http://www.aosabook.org/en/nginx.html

作者: Andrew Alexeev

出处:http://www.ituring.com.cn/article/4436

 

 

nginx(发音"engine x")是俄罗斯软件工程师Igor Sysoev开发的免费开源web服务器软件。nginx2004年发布,聚焦于高性能,高并发和低内存消耗问题。并且具有多种web服务器功能特性: 负载均衡,缓存,访问控制,带宽控制,以及高效整合各种应用的能力,这些特性使nginx很适合于现代网站架构。目前,nginx已经是互联网上第二流行 的开源web服务器软件。

14.1 为什么高并发重要

和十年前相比,目前的互联网已经难以想象的广泛应用和普及。从NCSAApache搭的web服务器提供的可点击的文本HTML,已然进化成超过 20亿人在线的通信媒介。随着永久在线的个人电脑,移动终端以及平板电脑的增多,互联网在快速变化,经济系统也完全数字有线化。提供实时可用信息和娱乐的在线服务变得更加复杂精巧。在线业务的安全需求也急剧变化。网站比从前更加复杂,需要在工程上做的更具有健壮性和可伸缩性。

并发总是网站架构最大的挑战之一。由于web服务的兴起,并发的数量级在不断增长。热门网站为几十万甚至几百万的同时在线用户提供服务并不寻常。十年前,并发的主要原因是由于客户端接入速度慢--用户使用ADSL或者拨号商务。现在,并发是由移动终端和新应用架构所带来,这些应用通常基于持久连接来 为客户端提供新闻,微博,通知等服务。另一个重要的因素就是现代浏览器行为变了,他们浏览网站的时候会同时打开46个连接来加快页面加载速度。

举例说明一下慢客户端的问题,假设一个Apache网站产生小于100KB的响应--包含文本或图片的网页。生成这个页面可能需要1秒钟,但是如果 网速只有80kbps10KB/s),需要花10秒才能把这个页面发送到客户端。基本上,web服务器相对快速的推送100KB数据,然后需要等待10 秒发送数据之后才能关闭连接。那么现在如果有1000个同时连接的客户端请求相同的页面,那么如果为每个客户端分配1MB内存,就需要1000MB内存来 为这1000个客户端提供这个页面。实际上,一个典型的基于Apacheweb服务器通常为每个连接分配1MB内存,而移动通信的有效速度也通常是几十 kbps。虽然借助于增加操作系统内核socket缓冲区大小,可以优化发送数据给慢客户端的场景,但是这并不是一个常规的解决方案,并且会带来无法预料 的副作用。

随着持久连接的使用,并发处理的问题更加明显。为了避免新建HTTP连接所带来的延时,客户端需要保持连接,这样web服务器就需要为每个连接上的客户端分配一定数量的内存。

因此,为了处理持续增长的用户带来的负载和更高量级的并发,网站需要大量高效的组件。而另一方面,web服务器软件运行在诸如硬件(CPU,内存, 磁盘),网络带宽,应用和数据存储架构等之上,这些基础设施显然也很重要。因而,随着同时在线数和每秒请求数的增长,web服务器性能也应该能够非线性扩 展。

Apache不再适用?

Apache web服务器软件发源于1990年代,目前在互联网网站上占有率第一。Apache的架构适合当时的操作系统和硬件,并且也符合当时的互联网状况:一个网 站通常使用一台物理服务器运行一个Apache实例。2000年之后,显然这种单服务器模型已经无法简单扩展来满足日益增长的web服务需求。虽然 Apache为新功能开发提供了坚实的基础,但他为每个新连接派生一个进程的做法(译注:Apache2.4版本起已经支持事件模型),不适合网站的非线性扩展。最终,Apache成为一个通用的web服务器软件,聚焦于功能多样化,第三方扩展开发,以及web应用开发的通用性。然而,当硬件成本越来越 低,每个连接消耗的CPU和内存越来越多,使用这样功能繁多的单一软件不再具有可伸缩性。

因而,当服务器硬件、操作系统和网络设施不再成为网站增长的主要限制因素时,网站开发者开始寻求更高效的手段来架设web服务器。大约十年前,著名 软件工程师Daniel Kegel提出:是时候让web服务器支持同时处理10000客户端了,并且预言了现在称为云服务的技术。KegelC10K设想明显推动了许多人 尝试解决这个问题--通过优化web服务器软件来支持大规模客户端连接的并发处理,nginx是其中做的最成功者之一。

为了解决10000个并发连接的C10K问题,nginx基于一个完全不同的架构更适合每秒同时连接数和请求数非线性增长。Nginx基于事件模 型,而没有模仿Apache为每个请求派生新进程或线程的做法。最终结果就是即使负载增加了,内存和CPU使用事件始终保持可预期。Nginx使用普通的 硬件就能在一个服务器上处理数万的并发连接。

Nginx的第一个版本发布之后,一般被用来同Apache一同部署,HTMLCSSJavaScript脚本和图片等静态内容由nginx处 理,来降低Apache应用服务器的并发和延时。随着开发演进的过程,nginx增加了FastCGIuswgeSCGI等协议的支持,以及对分布式 内存对象缓存系统如memcached的支持。也增加了其他有用的功能,例如支持负载均衡和缓存的反向代理。这些附加功能使nginx成为一个高效的工具 集,用于构建可伸缩的web基础设施。

20122月,Apache 2.4.x版本发布。虽然增加了新的并发处理核心模块和代理模块,用于加强可伸缩性和性能,但要说性能、并发能力和资源利用率是否能赶上或超过纯事件驱动 模型的web服务器还为时尚早。Apache新版本具有了更好的性能值得高兴,对于nginx+Apacheweb网站架构,虽然这能够缓解后端潜在的 瓶颈,但并不能解决全部问题。

nginx有更多的优点吗?

部署nginx最关键的好处就是能够高性能高效的处理高并发。同时,还有更多有意思的好处。

最近几年,web架构拥抱解耦的理念并且将应用层设施从web服务器中分离。虽然现在仅仅是将原先基于LAMP(Linux, Apache, MySQL, PHP, Python or Perl)所构建的网站,变为基于LEMPE表示Engine x)的。但是,越来越多的实践是将web服务器推入基础设施的边缘,并且用不同的方法整合这些相同或更新的应用和数据库工具集。

Nginx很适合做这些工作。他提供了必要的关键功能用于方便将下列功能从应用层剥离到更高效的边缘web服务器层:并发、长连接处理、SSL,静 态内容、压缩和缓存、连接和请求限速,以及HTTP媒体流等。Nginx同时也允许直接整合memcachedRedis或者其他的NoSQL解决方 案,增强为处理大规模并发用户的性能。

随着现代编程语言和开发包广泛使用,越来越多的公司改变了应用开发和部署的方式。Nginx已经成为这些改变范例之中的最重要的部件之一,并且已经帮助许多公司在预算内快速启动和开发他们的web服务。

Nginx开发始于2002年,2004年基于2-clause BSD授权正式对外发布。自发布起,Nginx用户就在不断增长,并且贡献提议,提交bug报告、建议和评测报告,这极大的帮助和促进了整个社区的发展。

Nginx代码完全用C语言从头写成,已经移植到许多体系结构和操作系统,包括:LinuxFreeBSDSolarisMac OS XAIX以及Microsoft WindowsNginx有自己的函数库,并且除了zlibPCREOpenSSL之外,标准模块只使用系统C库函数。而且,如果不需要或者考虑到 潜在的授权冲突,可以不使用这些第三方库。

谈谈关于Windows版本nginx。当nignxWindows环境下工作时,Windows版本的nginx更像是概念验证版本,而不是全 功能移植。这是由于目前nginxWindows内核架构之间交互的某些限制导致。Windows版本ngnix已知的问题包括:低并发连接数、性能降 低、不支持缓存和带宽策略。未来Windows版本的nginx的功能会更接近主流版本。

14.2 Nginx架构综览

传统基于进程或线程的模型使用单独的进程或线程处理并发连接,因而会阻塞于网络或I/O操作。根据不同的应用,就内存和CPU而言,这是非常低效 的。派生进程或线程需要准备新的运行环境,包括在内存上分配堆和栈、生成一个新的运行上下文。创建这些东西还需要额外的CPU时间,而且过度的上下文切换 引起的线程抖动最终会导致性能低下。所有这些复杂性在如Apache web服务器的老架构上一览无遗。在提供丰富的通用应用功能和优化服务器资源使用之间需要做一个权衡。

最早的时候,nginx希望为动态增长的网站获得更好的性能,并且密集高效的使用服务器资源,所以其使用了另外一个模型。受不断发展的在不同操作系统上开发基于事件模型的技术驱动,最终一个模块化,事件驱动,异步,单线程,非阻塞架构成为nginx代码的基础。

Nginx大量使用多路复用和事件通知,并且给不同的进程分配不同的任务。数量有限的工作进程(Worker)使用高效的单线程循环处理连接。每个worker进程每秒可以处理数千个并发连接、请求。

代码结构

Nginx worker的代码包含核心和功能模块。核心负责维护一个紧凑的事件处理循环,并且在请求处理的每个阶段执行对应的模块代码段。模块完成了大部分展现和应 用层功能。包括从网络和存储设备读取、写入,转换内容,进行输出过滤,SSI(server-side include)处理,或者如果启用代理则转发请求给后端服务器。

nginx模块化的架构允许开发者扩展web服务器的功能,而不需要修改nginx核心。Nginx模块可分为:核心、事件模块,阶段处理器,协 议、变量处理器,过滤器,上游和负载均衡器等。目前,nginx不支持动态加载模块,即模块代码是和nginx核心代码一起编译的。模块动态加载和ABI 已经计划在将来的某个版本开发。更多关于不同模块角色的详细信息可在14.4章找到。

NginxLinuxSolarisBSD系统上使用kqueueepollevent ports等技术,通过事件通知机制来处理网络连接和内容获取,包括接受、处理和管理连接,并且大大增强了磁盘IO性能。目的在于尽可能的提供操作系统建 议的手段,用于从网络进出流量,磁盘操作,套接字读取和写入,超时等事件中及时异步地获取反馈。Nginx为每个基于Unix的操作系统大量优化了这些多 路复用和高级I/O操作的方法。

14.1展示了nginx架构的高层设计。

 

Nginx架构_Nginx架构

14.1 nginx架构图

工作进程模型

前面提到过,nginx不为每个连接派生进程或线程,而是由worker进程通过监听共享套接字接受新请求,并且使用高效的循环来处理数千个连接。 Nginx不使用仲裁器或分发器来分发连接,这个工作由操作系统内核机制完成。监听套接字在启动时就完成初始化,worker进程通过这些套接字接受、读 取请求和输出响应。

事件处理循环是nginx worker代码中最复杂的部分,它包含复杂的内部调用,并且严重依赖异步任务处理的思想。异步操作通过模块化、事件通知、大量回调函数以及微调定时器等 实现。总的来说,基本原则就是尽可能做到非阻塞。Nginx worker进程唯一会被阻塞的情形是磁盘性能不足。

由于nginx不为每个连接派生进程或线程,所以内存使用在大多数情况下是很节约并且高效的。同时由于不用频繁的生成和销毁进程或线程,所以 nginx也很节省CPU时间。Nginx所做的就是检查网络和存储的状态,初始化新连接并添加到主循环,异步处理直到请求结束才从主循环中释放并删除。 兼具精心设计的系统调用和诸如内存池等支持接口的精确实现,nginx在极端负载的情况下通常能做到中低CPU使用率。

nginx派生多个worker进程处理连接,所以能够很好的利用多核CPU。通常一个单独的worker进程使用一个处理器核,这样能完全利用多 核体系结构,并且避免线程抖动和锁。在一个单线程的worker进程内部不存在资源匮乏,并且资源控制机制是隔离的。这个模型也允许在物理存储设备之间进 行扩展,提高磁盘利用率以避免磁盘I/O导致的阻塞。将工作负载分布到多个worker进程上最终能使服务器资源被更高效的利用。

针对某些磁盘使用和CPU负载的模式,nginx worker进程数应该进行调整。这里的规则比较基本,系统管理员应根据负载多尝试几种配置。通常推荐:如果负载模式是CPU密集型,例如处理大量 TCP/IP协议,使用SSL,或者压缩数据等,nginx worker进程应该和CPU核心数相匹配;如果是磁盘密集型,例如从存储中提供多种内容服务,或者是大量的代理服务,worker的进程数应该是1.5 2倍的CPU核心数。一些工程师基于独立存储单元的数目来决定worker进程数,虽然这个方法的有效性取决于磁盘存储配置的类型,。

Nginx开发者在下个版本中要解决的一个主要问题是怎么避免磁盘I/O引起的阻塞。目前,如果没有足够的存储性能为一个worker进程的磁盘操 作提供服务,这个进程就会阻塞在磁盘读写操作上。一些机制和配置指令用于缓解这个磁盘I/O阻塞的场景,最显著的是sendfileAIO指令,这通常 可以降低许多磁盘利用率。应该根据数据集(data set),可用内存数,以及底层存储架构等来规划安装nginx

当前的worker模型的另一个问题是对嵌入脚本的支持有限。举例来说,标准的nginx发布版只支持Perl作为嵌入脚本语言。这个原因很简单: 嵌入脚本很可能会在任何操作上阻塞或者异常退出,这两个行为都会导致worker进程挂住而同时影响数千个连接。将脚本更简单,更可靠地嵌入nginx, 并且更适合广泛应用的工作已经列入计划。

nginx 进程角色

Nginx在内存中运行多个进程,一个master进程和多个worker进程。同时还有一些特殊用途的进程,例如缓存加载和缓存管理进程。在 nginx 1.x版本,所有进程都是单线程的,使用共享内存作为进程间通信机制。Master进程使用root用户权限运行,其他进程使用非特权用户权限运行。

master进程负责下列工作:

  • 读取和校验配置文件
  • 创建、绑定、关闭套接字
  • 启动、终止、维护所配置数目的worker进程
  • 不中断服务刷新配置文件
  • 不中断服务升级程序(启动新程序或在需要时回滚)
  • 重新打开日志文件
  • 编译嵌入Perl脚本

Worker进程接受、处理来自客户端的连接,提供反向代理和过滤功能以及其他nginx所具有的所有功能。由于worker进程是web服务器每日操作的实际执行者,所以对于监控nginx实例行为,系统管理员应该保持关注worker进程。

缓存加载进程负责检查磁盘上的缓存数据并且在内存中维护缓存元数据的数据库。基本上,缓存加载进程使用特定分配好的目录结构来管理已经存储在磁盘上的文件,为nginx提供准备,它会遍历目录,检查缓存内容元数据,当所有数据可用时就更新相关的共享内存项。

缓存管理进程主要负责缓存过期和失效。它在nginx正常工作时常驻内存中,当有异常则由master进程重启。

Nginx缓存简介

Nginx在文件系统上使用分层数据存储实现缓存。缓存主键可配置,并且可使用不同特定请求参数来控制缓存内容。缓存主键和元数据存储在共享内存段 中,缓存加载进程、缓存管理进程和worker进程都能访问。目前不支持在内存中缓存文件,但可以用操作系统的虚拟文件系统机制进行优化。每个缓存的响应 存储到文件系统上的不同文件,Nginx配置指令控制存储的层级(分几级和命名方式)。如果响应需要缓存到缓存目录,就从URLMD5哈希值中获取缓存 的路径和文件名。

将响应内容缓存到磁盘的过程如下:当nginx从后端服务器读取响应时,响应内容先写到缓存目录之外的一个临时文件。nginx完成请求处理后,就 将这个临时文件重命名并移到缓存目录。如果用于代理功能的临时目录位于另外一个文件系统,则临时文件会被拷贝一次,所以建议将临时目录和缓存目录放到同一 个文件系统上。如果需要清除缓存目录,也可以很安全的删除文件。一些第三方扩展可以远程控制缓存内容,而且整合这些功能到主发布版的工作已经列入计划。

由于字数限制,后续内容请移步至

http://www.ituring.com.cn/article/4436查阅!