codecamp

卷2:第14章 NginX

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

作者: Andrew Alexeev

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

14.1 为什么高并发重要

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

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

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

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

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

Apache不再适用?

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

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

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

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

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

nginx有更多的优点吗?

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

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

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

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

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

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

谈谈关于Windows版本nginx。当nignx在Windows环境下工作时,Windows版本的nginx更像是概念验证版本,而不是全功能移植。这是由于目前nginx和Windows内核架构之间交互的某些限制导致。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章找到。

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

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

卷2:第13章 Moodle
卷2:第15章 Open MPI
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }