Nginx是一个高性能的HTTP和反向代理服务器和IMAP、POP3、SMTP服务器。Nginx 是由 Igor Sysoev 为俄罗斯访问量第二的 Rambler.ru 站点开发的,第一个公开版本0.1.0发布于2004年10月4日。其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。
Nginx以事件驱动的方式编写,所以有非常好的性能,但同时也是一个非常高效的反向代理、负载均衡服务器。在性能上nginx占用的系统资源较少,可以支持更多的并发连接,实现更高的访问效率。在功能上,nginx是优秀的代理服务器和负载均衡服务器,在安装配置上,nginx的安装简单、配置灵活。
Nginx支持热部署,启动速度特别快,还可以在不间断服务的情况下对软件版本或配置进行升级,即使运行数月也无需重新启动。
Nginx Web服务器最主要的就是各种模块的工作,模块从结构上分为核心模块、基础模块和第三方模块,其中三类模块分别如下:
用户根据自己的需求开发的模块都是属于第三方的模块。
Nginx从功能上分为如下三类:
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。因为作为反向代理服务器,每个并发会建立与客户端的连接和与后端服务的连接,会占用两个连接。
nginx在做反向代理的时候,提供性能稳定,并且能够提供灵活的转发功能。Nginx可以根据不同的正则表达式进行匹配,采取不同的转发策略,比如图片文件结尾的走文件服务器,动态页面走web服务器。nginx也可以对返回的结果进行错误跳转,异常判断等。如果备分发的服务器也存在异常,它可以将请求重新转发到另外一台服务器,然后去自动去除异常服务器。
正向代理:用户想从服务器拿取资源,但是只能够通过proxy服务器才能拿到,所以用户A只能去访问proxy服务器,然后通过proxy服务器去B拿取数据,是通过访问代理服务器最后访问外网的。正向代理最大的特点是客户端非常明确要访问服务器地址,服务器只是清楚来自那个一个代理服务器,而不清楚来自哪一个客户端,正向代理模式屏蔽或者隐藏了真实客户端的信息。
反向代理:反向代理其实就是客户端去访问服务器时,他并不知道会访问哪一台,感觉就是客户端访问了Proxy一样,而实则就是当proxy关口拿到用户请求的时候会转发到代理服务器中的随机(算法)某一台。而在用户看来,他只是访问了Proxy服务器而已,典型的例子就是负载均衡了。反向代理,主要用于服务器集群分布式部署的情况下,反向代理隐藏了服务器的信息!
Nginx提供的负载均衡策略有2种:内置策略和扩展策略
内置策略为轮询,加权轮询,IP hash。
Ip hash算法,对客户端请求的ip进行hash操作,然后根据hash结果将同一个客户端ip的请求分发给同一台服务器进行处理,可以解决session不共享的问题。
Nginx可以对不同的文件做不同的缓存处理,配置灵活,并且支持FastCGI_Cache,主要用于对FastCGI的动态程序进行缓存。配合着第三方的ngx_cache_purge,对制定的URL缓存内容可以的进行增删管理。