Nginx之一nginx特性及基础概念
Nginx的官方站点:www.nginx.org
Nginx:是engine X的简写
有两个分支比较流行:
- Tengine:淘宝对Nginx做了很多改进,做了很多研发。而且将其开源了,这个项目就是Tengine。
- Registry
Nginx在研发时调用了libevent这个组件,libevent是一个通用的高性能的网络库。它里边实现了epoil()这么一个调用,epoil()其实就是基于事件驱动的event模型的一个开发好的库文件。因此我们要想实现事件驱动模型,直接调用epoil()就可以了。而Nginx就调用了libevent这个高性能网络库中的epoil(),虽然我们说调用epoil(),事实上Nginx对这些众多的网络并发编程库支持的广泛程度还应该是超出想象的。
一、Nginx的特性
- 模块化设计、较好的扩展性;
因此作为程序员可以为Nginx设计第三方模块,不过与httpd不同的是,Nginx早期包括现在的版本虽然是模块化的,也就意味着说我们可以自行开发第三方模块对Nginx进行扩展,但是Nginx不支持模块动态装卸载的,也就是说编译的时候只能静态直接编译进Nginx而且随Nginx的启动而启动,即模块编译好之后可以直接使用,但只能直接编译成Nginx组成部分,只要Nginx启动,那么这个模块就一定会启动,它不支持动态装卸载。但是Tengine对Nginx的改进使得Tengine支持模块动态装卸载。
- 高可靠性
这是已经经过市场广泛验证的;它的高可靠性是靠其特殊的工作机制来实现的,其高可靠性依赖于主控进程与工作进程或工作线程的架构来实现的。虽然形容为特殊但是其实httpd也是这么工作的。Nginx的组成部分实际上是由一个主控进程master加多个子进程worker共同组成的,它有一个主控进程master,主控进程并不负责接收并响应任何用户请求,主控进程负责生成多个工作进程worker。其主控进程主要负责读取并验证即解析配置文件、创建绑定或关闭套接字、以及启动或终止维护worker进程的个数、还有无需重启进程让新配置文件中的新配置加载甚至于完成平滑版本升级等等。而worker子进程有多种种类,有的worker是实现缓存加载的,这在其反向代理模式中才有用、而有些是负责响应用户请求说白了就是接收传入并处理客户端的连接请求的、此外worker进程中还有一种进程叫做Cache Manager用来实现缓存管理。
- 低内存消耗
由于Nginx是使用一个内存响应n个请求的,所以它对内存的消耗非常低,有人做过统计说1w个保持连接keep-alive状态或模式下的connection,Nginx只需要消耗2.5M内存来维护;
- 支持热部署
所谓热部署指的是如果我们的配置文件更新了不用重启Nginx,新配置文件就能够生效了。甚至于Nginx的版本更新了,如现在用的是1.4的版本想升级到1.6,1.4的版本不用停机,1.6的慢慢就会切上去了。过后等1.6的切换完成了可以让1.4的再下线。
所以他支持热部署,因此这里的热部署指的是不停机而更新配置文件或者是更换日志文件即日志文件的滚动也包括升级程序版本等等都能实现。这就是热部署的功能。
- 支持事件驱动机制、AIO(异步IO)、mmap(内存映射机制);
二、Nginx的基本功能
虽然称Nginx为一个Web服务器,但是事实上它能支持多种功能,如:
- 静态资源的web服务器,能缓存打开的文件描述符;
最根本的它是静态资源的web服务器而不是一个应用程序服务器。说白了如果打开一个文件这个文件对应的描述符信息它能够缓存下来,所以从而使得我们再次访问同一个文件时打开的性能会被提升。
- http、smtp、pop3协议的反向代理服务器;
因此它既可以作为一个WEB服务器工作也可以作为一个WEB服务器的反向代理服务器工作。那反向代理服务器是什么呢?我们简单做一下介绍。
本来我们Web服务器的工作模式是在浏览器或客户端(User Agent)和服务器端之间直接进行的,所有的用户请求是直接从User Agent通过网络提交给Web服务器的。响应也是由Web服务器直接响应给客户端浏览器的。但是这样一来,用户将直接面对Web服务器,或者换句话来讲,Web服务器将直接面对众多的客户端,那也就意味着Web服务器自己记得负责建立连接维持连接还得负责处理用户请求,这样会使得Web服务器压力过大。
更重要的是,有些用户可能会攻击Web服务器。就像此前做nat一样,如果我们能够把用户和服务器之间加一个隔离层也照样能让用户请求能够到达Web服务器是不是可能会更好?因此这样一来我们可以再Web服务器的前端放一个主机,我们可以把这个主机称为代理。
什么叫代理呢?代理即它把自己声称为叫Web服务器,但是事实上它不是。所有用户请求直接到达给这个代理主机,这个主机自己也监听在80端口上也能够接受http的请求,但它自己并不负责提供任何内容,而是当用户请求到达这个服务器后,这个服务器自己负责把用户请求接进用户空间,因为他有一个进程,注意,它也有一个服务进程。这个服务进程称为代理进程,他解析用户请求并且判断一下这个客户端是不是允许访问的,如果不是,一解析之后直接拒绝了。这个请求甚至就不能到达Web服务器的。如果我们允许它访问,这个时候它也不会把用户的请求直接扔给后端服务器,而是它自己把用户的请求再改为封装成另外一种样子而后再向后端服务器发送。比如我们要发一个快递于是我们自己做了一个包装,这个包装扔给快递公司了,比如像什么顺丰,扔给快递公司之后快递公司需要打包,空运也好陆运也罢反正得帮你运过去,但是顺丰认为你的包装不够安全,运输过程当中可能会导致邮件损坏所以它把你的封装统统拆掉,再重新打包成它们认为安全可靠的封装,所以就这样做了改换。
因此当用户请求到达以后,这个服务器把用户请求分析完以后发现是可靠的,没问题,我可以帮你发给Web服务器让Web服务器进行响应,注意它自己没有任何内容而是它再一次把用户请求重新封装起来发给后端服务器。那这时候在后端服务器看来请求的发出者是中间这台主机,因为真正的请求是由他发出的,包括源地址也是中间服务器的地址,所以这样一来它就完全隔离的客户端和后端服务器。也就是说,它能够声称自己是Web服务器,接收用户对Web的请求,但是却又不负责提供任何内容而是转而把这个用户请求自己重新封装后交给后端服务器从后端服务器取回内容。但是各位应该知道,从后端服务器取回内容后,我们自己对取回的内容没有任何欲求,所以他带来的结果就是它会把这个取到的内容再一次把响应报文响应给客户端。所以客户端一看它向这个服务器发了请求服务器也响应给它了,但是事实上中间这台服务器并没有响应任何内容,它只是代为到后端服务器去取相关内容的。但是它把自己扮演成的确是那台服务器的样子,所以我们说这叫做反向代理。
而反代是可以n级的,即反向代理代理的也不是WEB服务器,它是另外更高一级的或更上一层的反向代理,可以代理n次。但是我们说过http协议当中有一个请求方法是TRACE,TRACE方法就是探测从请求这到最终服务器之间到底中间经过了哪些代理服务器。这些代理就称为反向代理。如下图:
那什么是正向代理呢?正向代理与反向代理类似,注意,反向代理通常只为一个或有限的一部分网站提供反代。但正向代理指的是,用户无论访问任何网站,都把对网站的请求提交给代理主机,代理主机自己无论用户请求的是什么网站,都自己作为客户端到互联网上去联系那台服务器,并且把取到的结果直接在本地封装以后或者在本地构建以后响应给客户端。所以所谓正向代理指的是它主要代表客户端请求任何网站;所谓反向代理服务器指的是它只是代为接收用户请求并且自行让到某些或者某个有限的服务器上去取那个内容的,而不是到任何网站服务器去内容。所以反代主要是把自己扮演成某个特定服务器的样子,而正向代理是把自己扮演成所有服务器的样子,可以这么去理解。
关于代理的目的,不光只是代理、不光是隔离它还有加速的作用,代理服务器还可以在本地提供缓存,刚才我们说过代理服务器自己到原始服务器上取到内容以后就构建了响应报文响应给请求者了。但是这样子当客户端请求同一个内容时他还得再来一遍,那为了避免这个过程,对于某静态内容不经常发生改变的内容,我们的代理服务器可以取到内容以后先缓存在本地。注意,缓存在本地的时候它是基于键值关系缓存的,有可能直接缓存在内存中,键通常是用户请求的URL,而值通常是对应的数据流,所以当同一个用户甚至是不同用户来请求同一段内容时,我们的代理服务器如果发现这个内容是可以被缓存,而且缓存尚未失效的话,那么他就从缓存中直接取得这个结果返回给客户端了,而不会向上游服务器或原始服务器发请求的。像这种他能够起到加速的作用并且极大的降低了或者减轻了后端服务器的压力。这就是所谓的反向代理。而Nginx能作为http、smtp、pop3这三种协议的反向代理,不光是Web,但是目前来看,众多的应用场景都是用在http协议上,smtp或POP被Nginx拿来反代的是很鲜见的。
- 支持缓存加速、负载均衡机制
刚才说过,缓存是用来加速的。
同时它还甚至于支持负载均衡机制,所谓负载均衡指的是在反代时后端的主机可以不止一个。若后端主机只有一个,用户请求直接交给后端,但是后端服务器实在是太忙了,扛不住,因为它有可能是提供动态内容的,基于cgi模块或fastcgi模型,有可能是一个httpd服务器,直接提供动态的脚本,向php内容提供。而前端服务器作为Nginx来讲,可以轻易扛得住1万个并发请求,请求连接进来它能够帮你接进来。但是它把每一个请求都转发给后端服务器,一个服务器最多并发到1千个就相当不错了,如果实在扛不住,怎么办呢?
可以多加几个后端服务器,因此,它可以把一部分用户请求发到第一台服务器,另外一部分发到第二个,在一部分发到第三个服务器上。所以使得众多前端用户请求被分发至后端多台主机上来了,这就是一种负载均衡机制。而前面这台主机既是代理同时又是一个负载均衡调度器。但是作为调度器负载均衡时,和只反代一个主机时,它们两个所需要解决的问题其实它的复杂度是相去甚远的。
为什么呢?各位可以明白,我们给予负载均衡机制时,如果第一个用户过来请求时,假设这是一个电商站点,我们给它转发到第一台服务器上去了。它在第一个服务器上登录了然后再购物车中加了一堆商品,大家知道,http协议是无状态的,过一会连接断开,他一刷新,又被重新分发至第二台主机上来了。第二台主机上没有它的购物车,也没有它的相关信息。那因此这显然是没办法接受的,所以一旦要支持负载均衡机制,那么session保持,就是此前用户曾经建立的会话如何执行并进行保持将成为一个非常重要亟待解决的问题。
那如何解决这个问题,我们有多种方案,后面讲到负载均衡时会详述这里的解决方案。但不管怎么讲,各位应该知道,这是Nginx另外的基本功能,它既能实现缓存加速又能实现负载均衡。当然缓存加速也主要是在反代时实现,负载均衡亦是如此。
- 支持fastcgi协议,uWSGI(Python)等
那也就意味着,能够跟基于fpm模式工作的php服务器结合起来提供LNMP。LNMP指的是Nginx、MariaDB、PHP等相关对应的结合。其实除了fastcgi之外还有像uWSGI协议实现跟Python语言所研发的Web服务器应用程序进行交互等等。
- 支持模块化(但是是非DSO机制的)、过滤器(用户可以定义过滤器只对某些内容进行特殊处理,像zip能够实现对特定文本进行压缩)、SSI(服务器端包含)机制、图像的大小调整(在访问一个电商站点时,它老是给我们一些图片,然后将鼠标放上去它能够自动扩大为一个清晰的高清大图给我们展示等等);
- 支持SSL;
从而能够提供https服务;
三、Nginx的扩展功能
- 基于名称和IP的虚拟主机;
当然它也支持基于端口的虚拟主机;
- 支持keepalive;
- 支持平滑升级;
- 定制访问日志、支持使用日志缓冲区提高日志缓冲性能;
- 支持url rewrite;
- 支持路径别名;
- 支持基于IP及用户的访问控制;
Nginx基于用户的访问控制需要借助于httpd的htpassword来实现。
- 支持速率限制,支持并发数限制;
说白了就是来自于同一个IP我们最多能够发起几个并发连接请求,同时来自于同一个IP最多能够发起多大的访问速率或者我给你传输数据时速率最多为多少等等。
事实上,Nginx有的功能,httpd都有,而httpd有的Nginx未必有,所以目前来看,Nginx取代不了httpd。
四、Nginx的基本架构(基本架构特性)
- Nginx是由一个master进程,称为主进程,负责生成一个或多个worker进程或线程
所以它的工作模式我们可以理解为是这样的:
Nginx是由一个master进程,称为主进程,负责生成一个或多个worker进程或线程,这些worker进程才是真正去负责去接收并处理用户请求的。
那如果基于反代的话它还能够基于反代的Cache来实现加速,而既然有Cache了,大家知道缓存它总然会有一个时刻会失效的,每一个缓存都有一个存活期限,那因此Cache Loader负责加载缓存,而Cache manager负责过期以及清理缓存等。
那么事实上worker与本地进程磁盘IO是支持AIO、mmap(内存映射)、各种高级IO机制、甚至还支持sendfile机制。
另外它还支持http协议、fastcgi协议、memcache等等跟各种后端存储进行打交道,它还可以作为反代向后进行打交道的。
而此前提到的几个Nginx特性此处也有展示:事件驱动、异步、非阻塞。
- 支持事件驱动,支持epoll、kqueue、/dev/poll机制,而且支持默认使用边缘触发机制;
epoll机制是Linux上实现的事件驱动,但是如果使用的是BSD或者是Unix的话就不一定了。如果使用的是BSD则使用的是kqueue机制,如果是Servers的话,则用到的是/dev/poll。epoll、kqueue、/dev/poll这三个都是事件驱动模型,第一个是Linux上用的,第二个是BSD上用的,第三个是Servers上用的。但它们都是实现了类似的功能,而Nginx几乎都支持。
另外IO复用的时候,它支持select、poll、rt signal,也叫IO复用器。
- 支持sendfile、sendfile64;
sendfile默认情况下只能发送很小的文件可能最大不会超过几个K,而sendfile64可以支持更大的文件,可以在内核中直接实现响应。它们的大小上限是有要求的。
- 支持AIO;
- 支持mmap
sendfile:
作为一个静态内容来讲,当用户请求进来时它一定要先进入内核,因为用户的请求要先进入网卡设备。到达网卡设备后这个请求会通过注册监听在80端口上的内核套接字绑定,根据用户请求的目标端口从而使得实现向用户空间的Web服务器进行转发。WEB服务器收到请求后发现用户请求了某一个静态资源,于是它向内核发起IO请求,内核负责把文件从磁盘加载进内核内存,而后从内核内存复制给进程内存。接着进程接下来就应该响应这个用户请求了,那怎么响应啊?
构建响应报文,内容又从进程内存发往内核内存,因为不发往内存它怎么能够通过网卡响应给客户端,而我们说过能调用网卡的只有内核,所以它又一次发起IO调用,只不过这一次IO是网络IO而不是磁盘IO。发起调用时会把数据从自己的进程内存空间发往内核内存空间,放到网卡发送队列中去进而通过网卡设备发出去了。
在这个过程当中,我们的数据是怎么走的?数据的走向,首先从磁盘到内核,从内核到用户空间,从用户空间又回到内核了。其实没有做什么改变,直接响应过去了,除了加了一个响应报文之外就发过去了。我们可以发现这两次IO过程除了白白浪费时间是没有任何更好的效果的。那我们能不能这样,当这个资源从内核取得之后直接就从内核响应给客户端得了,数据从内核到磁盘内存而后直接构建一个响应报文直接响应给客户端了。而这种机制就叫做sendfile。
当然,sendfile并不是一种多么新鲜的机制,httpd也支持,不光是Nginx。
五、Nginx的工作模式
它与前面对应的httpd的prefork、worker、event相比较而言,它的模式就是:
非阻塞、事件驱动、由一个master进程生成多个worker线程,每个worker响应n个请求。
所以如果作为Web服务器而言,他总体能够支持的响应并发数等于worker * n;如果生成了10个worker,每个worker能响应1万个,那一共就可以响应10万个。当然了,Nginx没有这么大的能力,因为每一个请求或连接进来我们都得给它一个套接字,因此套接字所谓TCP来讲最大数量端口也只有65535个,在考虑到其它服务在用,它系统在保留一些,所以能使用5万个就不错了。因此Nginx没准在有些极端场景中,据说有人曾经使用Nginx最大支持单机并发达到5.2万个。虽然这是理论值,但是国内有人让它很轻松支持3万个是没问题的。再多就有困难了。
不过还有特别强调的是,在反代模型下,有可能会更少。
六、Nginx的模块类型
Nginx刚才说过它是模块化的,因此有众多模块,那模块类型有哪些呢?无非就这样几个:(在官方文档中是这么分类的)
- 核心模块
- Standard HTTP modules(标准的http协议模块)
- Optional HTTP modules(可选的http协议模块)
- Mail modules
- 3rd party modules
注意,前四种模块Nginx都自带,第五种模块,也就是所谓的第三方模块我们需要在编译Nginx的时候自己手动指定模块在何处,自己手动指明模块文件。而后在编译时才能把它编译成Nginx的组成部分。
七、Nginx的安装方法
Nginx已然成为一个非常流行的Web服务器解决方案了,所以Nginx本身虽然没有直接被收入进我们CentOS系统,但也已经被收入进EPEL源了。
使用yum查看是否有相关的包:
安装方法:
- 源码:编译安装;
- 制作好的程序包:如rpm包、deb包;
此处先演示一下如何源码编译Nginx。因为不同的公司对于Nginx的定制安装是有着不同要求的。
下载源码包:
编译安装程序包时,需要提供好最基本的开发环境。
注意:Nginx哪怕已经提供了开发环境时,也会有可能不会自动安装的一个依赖的软件包叫pcre-devel
说明:它要用到pcre-devel来实现poll扩展的这么一种表达式以实现URL重写。一般来讲,URL重写的功能如果我们需要用到,就有可能用到pcre-devel,所以建议把这个安装上。
查看配置选项:
选项说明:
--prefix=PATH:默认安装路径 --sbin-path=PATH:Nginx主程序的安装路径 --conf-path=PATH:主配置文件的路径 --error-log-path=PATH:错误日志路径 --pid-path=PATH:pid文件路径 --lock-path-PATH:锁文件路径 --user=USER:Nginx启动后它的worker会以哪个普通用户的身份来运行,一般来讲工作线程都应该以普通用户的身份运行。 --group=GROUP --with-…:表示启用这个功能的,需要注意的是,提示为with表示默认可能没启用;要启用使用with; --without-…:表示禁用这些功能的,而提示为without的表示默认启用了,要想禁用使用without; --http-client-body-temp-path=PATH:http协议如果对方使用的是PUT或POST方式请求,客户端会提交一些内容过来,这个内容通过网络传输一些数据它是流式化的,
是一个一个数据传过来的。因此如果传输的数据很大的话,客户端上传一个2G的数据我们不能将其都放在内存中等待接受。
所以我们可以把客户端上传的内容先放在一个临时目录中所以这是我们定义临时目录位置的。所以这是客户端提交数据时临时存放的文件的路径。 --http-proxy-temp-path=PATH:作为代理服务器时代理内容,因为代理服务器我们要从服务器取得内容在本地做处理以后在打包给客户端,他也需要用到一个临时目录。 --http-fastcgi-temp-path=PATH:fastcgi协议; --http-uwsgi-temp-path=PATH:uwsgi协议; --http-scgi-temp-path=PATH:scgi协议 注意:上述协议在反代时也都各自需要一些工作空间,我们可以指定它们的工作时临时使用的临时路径。不指定也没有关系,它自己会找一个位置当成临时路径的。 --with-pcre:支持URL重写时,基于pcre能够支持更强大的正则表达式引擎; --with-openssl=DIR:支持使用openssl,仅用于指明openssl在什么地方的,其实我们要启用对https的支持要是用的是—with-http-ssl_module模块的,
默认是没启用的,需要使用with启用。
编译安装
创建用户用于运行Nginx进程:
[root@localhost nginx-1.6.3]# ./configure --prefix=/usr/local/nginx --conf-path=/etc/nginx/nginx.conf --user=nginx --group=nginx --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx/nginx.pid --lock-path=/var/lock/nginx.lock --with-http_ssl_module --with-http_stub_status_module --with-http_gzip_static_module --with-http_flv_module --with-http_mp4_module --http-client-body-temp-path=/var/tmp/nginx/client --http-proxy-temp-path=/var/tmp/nginx/proxy --http-fastcgi-temp-path=/var/tmp/nginx/fastcgi --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi
注意:为了能够便于实现将来当系统损坏时,重装系统后配置文件存在有两种途径可以解决这个问题:要么放在etc下,将来得定期备份;要么就干脆不指定了就放在安装路径下这是最合理的做法。很多时候我们将来安装时并不会把它的配置文件路径都指到/etc目录下去,而就放在安装目录下。这样会使得我们将来迁移整个程序会变得非常容易。那此处仍然遵循风格,比如说为了我们将来使用时方便仍然指定为/etc/nginx/nginx.conf,专门指定一个特定目录。注意一旦把主配置文件指在此处,那我们主配置文件所用到的其它辅助配置文件也都会放在同一个目录下,所以我们这里打开一个单独的目录来指明。
查看是否有程序监听80端口,若没有直接启动即可:
测试访问:
至此,安装完成!
八、Nginx配置文件
worker_process:指定启动的worker进程数;
有配置指令,而且有些会有花括号,花括号表示这是一个配置段,只对其中一部分内容生效。其中有一部分内容是直接写在配置文件中,顶格写的。有一部分是events定义一个容器后里面可以定义一部分内容;有一个是http甚至还有其它内容。
所以整个Nginx的配置文件主要由这样几部分组成:
- main配置段:全局配置段;
说白了就是对于其他配置段都是有效的;
- event{}配置段:定义event模型的工作特性;
- http{}:定义http协议相关的配置;
所有跟web服务相关的配置都只能定义在http配置段当中;
注意:Nginx的所有配置指令一般而言是不区分大小写的,但是要以分号结尾。否则就是语法错误
语法格式:
directive value1 [value2…]
Nginx支持使用变量
变量分为两类:
- 内置变量
内置变量是由模块自带的,每引入一个模块,这个模块都有可能提供一些新变量。所以模块会提供内建变量的定义;那核心模块提供一堆变量,其它模块像标准的TCP/IP模块也会提供一堆的变量。
- 自定义变量
在配置文件中使用set var_name value表示给它定义了一个变量并定义了一个值。而后我们就可以在配置文件的其他位置进行调用了。
其实,我们要想能够配置Nginx就需要不断的修改它的配置文件来实现,而它的配置文件刚才我们说过有主配置段、event配置段、http配置段组成。main配置段的指令配置非常多,Nginx的指令参数大概有上百个之多,我们不可能一一去了解它,也没必要一一去了解,只了解一些关键的就行。但是尽管这么讲,它的主配置段的指令大体可以分为四类:
- 用于调试、定位问题的指令;
- 正常运行必备的配置;
- 优化性能的配置;
- 事件相关的配置;
注意:事实上,event也算作是main配置的组成部分,因为event那一段对所有的协议包括http、smtp等等也都一样是生效的。
接下来就对Nginx的每一个指令功用或者是最核心的指令使用机制、功用来做介绍。其中主配置段的指令当中有很多个,讲完之后接下来就将Nginx自己的配置,比如如何使用虚拟主机、如何后使用别名、如何定义location、如何定义URL重写等等。