从现在开始学习Nginx——1.基础知识
作为高速、轻量、高性能等优点集于一身的服务器,Nginx在近些年迅速发展并不断扩大市场份额,甚至在最近其市场份额一举超过微软的IIS,跃身到第二位,仅次于Apache。
但是由于其高性能的特点,尤其适用于高流量网站,在全球前 10,000个站点中的市场份额为58.4%,稳居第一。
Nginx的诞生
Nginx是俄罗斯人Igor Sysoev在2002年开发的一个HTTP站点服务器,它被声称可以每天处理5亿个请求。并于2004年公开,使用BSD开源协议,能运行在几乎所有主流的操作系统上。
在Nginx诞生的年代,软件界中有一个问题被大家讨论得最为热烈,那就是C10K问题(如何解决10万个客户端的并发请求问题)。
即使当时的硬件规格依旧在不断上升,但是如果像Apache HTTP那样对于每一个请求都要单独分成一个进程或者线程来执行,进程号或者线程堆栈这些系统资源将不可避免地会被耗尽。因此,轻量级服务器势在必行。
在当时,为了解决C10K问题,还有其它两款轻量级Web服务器被广泛关注————lighttpd和Boa,但是它们的表现性能都难以企及Nginx。
Nginx的反向代理
Nginx能在众多轻量级服务器中脱颖而出的一个很重要的原因,就是它拥有超强的反向代理能力。
不同于一般中小型网站的服务器直接接受用户请求,然后运行对应的PHP、Java等程序代码,反向代理是把反向代理服务器作为接受用户请求的终端,然后在其后方在配置若干真正提供服务的web应用服务器。
反向代理服务器一般会根据请求的任务类型和备选逻辑服务器的负载情况进行任务分发(负载均衡)。
除此以外Nginx支持HTTPS的SSL/TLS协议,可以为本来不具备此功能的应用添加HTTPS支持(用户与反向代理服务器通过有加密功能的HTTPS进行通信,反向代理服务器再通过安全的内网直接和各逻辑服务器通信)。
同时,反向代理服务器还能缓存各种静态资源(如图片文件和HTML文件),这使得它十分善于处理大量的重复请求。
除了上面提到的最主流的HTTP/HTTPS以外,Nginx也能处理SMTP、IMAP、POP3等协议的负载均衡。甚至很快也能支持“下一代的HTTP”——SPDY协议。
谈谈模块的扩展
因为Nginx是一个轻量级的Web服务器,因此除了反向代理和资源缓存等核心功能外,要尽可能地避免添加不必要的功能模块。
当然,Nginx不乏许多优秀的标准库和第三方模块,比如流量和连接监控和限制、图像格式转换等等。
但是,如果要添加新模块的话,必须将整个程序重新编译。虽然这样不如Apache HTTP的动态加载来得方便,但是可以保证运行效率的最大化和占用资源的最小化。
Nginx一般有各种各样的安装包,里面附带有不尽相同的非核心模块,在安装前务必了解清楚。
如何处理动态资源
Nginx很擅长负载均衡和处理HTML文件和图片等静态资源,但是除了SSI(Server Side Include)技术以外基本不能够自己动态地生成资源。
这时,其实我们可以通过UNIX Domain Socket来让其他进程处理并返回对应的动态资源。如果该进程位于其他机器上,则可以通过TCP来进行通信。
特别地,对于和PHP程序之间的通信可以使用FastCGI,对于Python可以使用uWSGI,对于Ruby on Rails可以使用Phusion Passenger。
高性能的秘诀————事件驱动
Nginx之所以能同时处理大量的请求,原因在于它采用了十分巧妙的事件驱动机制。
作为一个Web服务器,要同时处理多个请求,不可避免地要面对这么一个问题,如何同时处理像磁盘和网络等等的I/O请求,即如何实现I/O复用。
为了解决该问题,操作系统在很久之前就开始提供诸如“select”、“poll”等系统调用。Apache HTTP的多处理模块(MPM,multi-processing module)就会用到这些系统调用。
但是,select/poll为了识别出哪些文件或者socket已经准备就绪,必须将所有已注册的文件描述符(fd)一个个地检查一遍。如果注册列表越长,那么每次的扫描所需的时间也越长。
而Nginx的I/O复用机制使用的是“epoll”这个基于事件驱动的系统调用。因为epoll会在系统内核管理和监听这些文件描述符(fd),并自动把就绪的加入到Ready队列当中。
所以,程序只需在需要时往Ready队列中取出一个进行处理即可,而不用切换到内核态,然后一个个地检查,然后又切换回用户态。
这样,无论需要注册监听的I/O有多少,都不会影响程序的运行效率。
为了避免select/poll带来的线性增长的负担,Apache HTTP必须将这些IO分散到各个进程/线程中处理,这样势必会造成占用内存的增长。
但是,Nginx可以通过利用“epoll”,保证可以使用一个进程/线程完成所有请求的处理,这样可以大大减少内存的占用,从而使应对上万并发请求成为可能。