1. nginx简介
Nginx是一个高性能的HTTP和反向代理服务器和IMAP、POP3、SMTP服务器。Nginx 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本0.1.0发布于2004年10月4日。其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。
Nginx以事件驱动的方式编写,所以有非常好的性能,但同时也是一个非常高效的反向代理、负载均衡服务器。在性能上nginx占用的系统资源较少,可以支持更多的并发连接,实现更高的访问效率。在功能上,nginx是优秀的代理服务器和负载均衡服务器,在安装配置上,nginx的安装简单、配置灵活。
Nginx支持热部署,启动速度特别快,还可以在不间断服务的情况下对软件版本或配置进行升级,即使运行数月也无需重新启动。
2. nginx的工作原理
Nginx Web服务器最主要的就是各种模块的工作,模块从结构上分为核心模块、基础模块和第三方模块,其中三类模块分别如下:
- 核心模块:HTTP模块、EVENT模块、MAIL模块
- 基础模块:HTTP Access模块、HTTP FastCGI模块、HTTP Proxy模块和HTTP Rewrite模块
- 第三方模块:HTTP Upstream Request Hash模块、Notice模块、HTTP Rewrite Key模块
用户根据自己的需求开发的模块都是属于第三方的模块。
Nginx从功能上分为如下三类:
- Handlers(处理器模块):此类模块直接处理请求,并进行输出内容和修改headers信息等操作。Handlers处理器模块一般只能有一个。
- Filters(过滤器模块):此类模块主要对其他处理器模块输出的内容进行修改操作,最后由nginx输出
- Proxies(代理类模块):此类模块是Nginx的HTTP Upstream之类的模块,这些模块主要与后端一些服务比如FastCGI等进行交互,实现服务代理和负载均衡等功能。
Nginx本身做的工作其实不多,当它接到一个HTTP请求的时候,它仅是通过查找配置文件将此请求映射到一个location block,而此location中所配置的各个指令则会启动不同的模块去完成工作。通常一个location中的指令会涉及到一个handler模块和多个filter模块(多个location可以重复用同一个模块)。handler模块负责处理请求,完成响应内容的生成,而filter模块对响应内容进行处理。如图:
nginx的高并发得益于其采用了epoll模型,与传统的服务器程序架构不同,epoll是linux2.6以后才出现的,Nginx采用epoll模型,异步非阻塞,而apache采用的是select模型。
select的特点:select选择句柄的时候,是遍历所有的句柄,也就是说句柄有事件响应的时候,select需要遍历所有的句柄才能获取到哪些句柄有事件通知,因此效率比较低。
epoll的特点:epoll对于句柄事件的选择不是遍历的,是事件响应的,就是句柄上事件来就会马上选择出来,不需要去遍历整个句柄链表,因此效率比较高。
Nginx默认是80端口,启动一个master进程,同时有master进程生成多个工作进程,当浏览器发起一个HTTP连接请求,每个进程都有可能处理这个连接,怎么做到的呢?怎么保证同一时刻一个HTTP请求被一个工作进程处理呢。
首先每个worker进程都是从Master进程fork出来,在Master进程里面,建立好需要listen的socket(listenfd)之后,会fork出多个worker进程。所有worker进程的listenfd会在新连接到来时变得可读,为保证只有一个进程处理该连接,所有worker进程在注册listenfd读事件前抢accept_mutex,抢到互斥锁的那个进程注册listenfd读事件,在读事件里调用accept接受该连接。
Nginx默认采用多进程工作方式,Nginx启动后,会运行一个master进程和多个worker进程。其中master充当整个进程组与用户的交互接口,同时对进程进行监护,管理worker进程来实现重启服务、平滑升级、更换日志文件、配置文件实时生效等功能。worker用来处理基本的网络事件,worker之间是平等的,他们共同竞争来处理来自客户端的请求。
当一个worker进程在accept这个连接之后,就开始读取请求、解析请求、处理请求,产生数据后,再返回给客户端,最后才断开连接,这样形成一个完整的请求流程。如图:
请求到来时,如何分配均分worker进程来处理他们?
在创建master进程时,先建立需要监听的socket(listenfd),然后从master进程中fork()出多个worker进程,如此一来每个worker进程多可以监听用户请求的socket。一般来说,当一个连接进来后,所有在Worker都会收到通知,但是只有一个进程可以接受这个连接请求,其它的都失败,这是所谓的惊群现象。nginx提供了一个accept_mutex(互斥锁),有了这把锁之后,同一时刻,就只会有一个进程在accpet连接,这样就不会有惊群问题了。
先打开accept_mutex选项,只有获得了accept_mutex的进程才会去添加accept事件。nginx使用一个叫ngx_accept_disabled的变量来控制是否去竞争accept_mutex锁。ngx_accept_disabled = nginx单进程的所有连接总数 / 8 -空闲连接数量,当ngx_accept_disabled大于0时,不会去尝试获取accept_mutex锁,ngx_accept_disable越大,于是让出的机会就越多,这样其它进程获取锁的机会也就越大。不去accept,每个worker进程的连接数就控制下来了,其它进程的连接池就会得到利用,这样,nginx就控制了多进程间连接的平衡。
每个worker进程都有一个独立的连接池,连接池的大小是worker_connections。这里的连接池里面保存的其实不是真实的连接,它只是一个worker_connections大小的一个ngx_connection_t结构的数组。并且,nginx会通过一个链表free_connections来保存所有的空闲ngx_connection_t,每次获取一个连接时,就从空闲连接链表中获取一个,用完后,再放回空闲连接链表里面。一个nginx能建立的最大连接数,应该是worker_connections * worker_processes。当然,这里说的是最大连接数,对于HTTP请求本地资源来说,能够支持的最大并发数量是worker_connections * worker_processes,而如果是HTTP作为反向代理来说,最大并发数量应该是worker_connections * worker_processes/2。因为作为反向代理服务器,每个并发会建立与客户端的连接和与后端服务的连接,会占用两个连接。
3. nginx的特点
- 跨平台:Nginx可以再大多数Unix like OS编译运行,windows上也可以
- 配置简单,上手容易
- 非阻塞、高并发连接、事件驱动:select,poll, epoll, kqueue,/dev/poll
- 事件驱动
- master/worker结构
- 内存消耗较少
- 成本低廉
- 内置健康检查功能
- 节省带宽
- 稳定性高
- 支持mmap
- 支持文件AIO(异步I/O)
- 支持sendfile,sendfile64
- nginx代理和后端WEB服务器间无需长连接
- 接收用户请求是异步的,就是说先将用户请求全部接收下来,再一次性发送后端web服务器,极大的减轻了后端web服务器的压力
- 发送响应报文时,是边接收来自后端web服务器的数据,边发送给客户端
- 网络依赖低,nginx对网络的依赖程度非常低,理论上只要能ping通就可以实施负载均衡,可以有效的区分内网和外网流量
- 支持服务器检测,nginx能够根据应用服务器处理页面返回状态码、超时信息等检测服务器是否出现故障,并及时返回错误的请求重新提交到其他的节点上
4. nginx常用的功能
4.1 HTTP代理,反向代理
nginx在做反向代理的时候,提供性能稳定,并且能够提供灵活的转发功能。Nginx可以根据不同的正则表达式进行匹配,采取不同的转发策略,比如图片文件结尾的走文件服务器,动态页面走web服务器。nginx也可以对返回的结果进行错误跳转,异常判断等。如果备分发的服务器也存在异常,它可以将请求重新转发到另外一台服务器,然后去自动去除异常服务器。
正向代理:用户想从服务器拿取资源,但是只能够通过proxy服务器才能拿到,所以用户A只能去访问proxy服务器,然后通过proxy服务器去B拿取数据,是通过访问代理服务器最后访问外网的。正向代理最大的特点是客户端非常明确要访问服务器地址,服务器只是清楚来自那个一个代理服务器,而不清楚来自哪一个客户端,正向代理模式屏蔽或者隐藏了真实客户端的信息。
反向代理:反向代理其实就是客户端去访问服务器时,他并不知道会访问哪一台,感觉就是客户端访问了Proxy一样,而实则就是当proxy关口拿到用户请求的时候会转发到代理服务器中的随机(算法)某一台。而在用户看来,他只是访问了Proxy服务器而已,典型的例子就是负载均衡了。反向代理,主要用于服务器集群分布式部署的情况下,反向代理隐藏了服务器的信息!
4.2 负载均衡
Nginx提供的负载均衡策略有2种:内置策略和扩展策略
内置策略为轮询,加权轮询,IP hash。
- weight轮询(默认):接收到的请求按照顺序逐一分配到不同的后端服务器,即使在使用的过程中,某一台机器宕机了,nginx会自动把该服务器剔除队列,请求受理不会收到任何影响。这种方式可以给不同的服务器设置一个权重值,用于调整不同的服务器上请求的分配率,权重数据越大,被分配到请求的几率越大。
- ip_hash:每个请求按照发起客户端的ip的hash结果进行匹配,这样的算法下一个固定IP地址的客户端总会访问到同一个后端服务器,这在一定程度上解决了集群部署环境下session共享的问题。
- fair:智能调整调度算法,动态的根据服务器的请求处理到响应时间进行均衡分配,响应时间短处理效率高的服务器分配到请求的概率会更高,相反处理时间长处理效率低分配到的请求会少。结合了前两者的优点的一种调度算法。但是需要注意的是nginx默认不支持fair算法,如果要使用这种调度算法,请安装upstream_fair模块。
- url_hash:按照访问的url的hash结果分配请求,每个请求的url会指向后端固定的某个服务器,可以在nginx作为静态服务器的情况下提高缓存效率。同样要注意nginx默认不支持这种调度算法,要使用的话需要安装nginx的hash软件包
Ip hash算法,对客户端请求的ip进行hash操作,然后根据hash结果将同一个客户端ip的请求分发给同一台服务器进行处理,可以解决session不共享的问题。
4.3 web缓存
Nginx可以对不同的文件做不同的缓存处理,配置灵活,并且支持FastCGI_Cache,主要用于对FastCGI的动态程序进行缓存。配合着第三方的ngx_cache_purge,对制定的URL缓存内容可以的进行增删管理。