从“Nginx”的世界路过

2018-04-01 09:17 阅读 2,475 次 评论 0 条

Nginx是一款由俄罗斯程序员Igor Sysoev所开发的轻量级WEB服务器、反向代理服务器以及电子邮件(IMAP/POP3)代理服务器。相较于Apache、lighttpd具有占用内存少、稳定性高等优势,依靠其强大的并发能力、丰富的模板库以及友好灵活的配置而闻名。

Nginx的Master-Worker模式

以我的阿里云ECS(lnmp环境)为例,启动nginx服务后,会在80端口启动socket服务进行监听,可以使用netstat来查看这个监听端口:

Nginx之所以被称为高性能服务器,这与他的设计架构与工作原理是密不可分的。如下图所示,它采用master进程和worker协同工作:

Nginx在启动之后,会有一个master进程和多个worker进程,两者的作用如下:

master进程:读取并配置文件nginx.conf;管理worker进程:向多个worker进程发送signal,监控workder进程的运行状态,当worker进程异常退出时,会自动启动新的worker进程。

worker进程:为了避免线程切换,每一个worker进程都维护一个线程来处理连接和请求。多个worker进程之间是对等的,他们同等竞争来自client的请求,并且一个请求只能在一个worker进程中处理(进程间的相互独立性)。worker进程的个数由conf文件决定,一般和CPU的核心数一致。

如果你对上述文字描述不解,那我们模拟一下master-worker的工作原理流程图:

下述代码是从nginx的源码提取出来的master工作部分,可以看出,master进程中的for(;;)死循环内有一个关键的sigsuspend()函数调用,该函数调用使得master进程的大部分时间都处于挂起等待状态,直到master进程接收的信号为止:

相较于master,worker进程就显得简单多了:它的主要任务是完成具体的任务逻辑,即client或server之间的数据读取、I/O交互事件,所以worker进程的阻塞点是在像select()、epoll_wait()等这样的I/O多路复用函数调用处,以等待发生数据可读/写事件,以及被可能收到的进程信号中断。

Nginx如何做到热部署

鉴于master管理进程与worker工作进程的分离设计,使得nginx具备热部署的功能。所谓热部署,就是对nginx.conf进行修改后,不需要restart nginx,也不需要中断请求,就能让配置文件生效。Nginx对此的做法是:在修改配置文件nginx.conf后,重新生成新的worker进程,当然会以新的配置进行处理,至于旧的worker进程,等执行完以前的请求后,发送信号kill即可。

因此在7*24小时不间断服务的前提下,就可以对Nginx服务器升级、修改配置文件、更换日志文件等操作。

Nginx的反向代理服务

在介绍反向代理之前,我先给大家科普一下正向代理:它更像是一个跳板,代理访问目标资源。比如你想在Youtube上看视频、上Google搜索信息,但是直接访问肯定是不行的,它们的服务器都设立在国外,这时你需要连接上可以访问国外网站的代理服务器,通过代理服务器获取到资源后,然后返回给你。

总结来说:正向代理是一个位于client和目标服务器之间的代理服务器,client向代理发送一个请求并指定目标服务器的ip和port,然后目标服务器将请求获取的内容通过代理再返回给client(注意:client需要设置代理的ip和port)。

正向代理的主要作用如下:

反向代理:以代理服务器来接收Internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给Internet上请求连接的client,此时代理服务器对外就表现为一个服务器。反向代理对外是透明的,访问者并不知道自己访问的是一个代理,client也是无感知代理的存在的,因此客户端是不需要任何配置就可以直接访问的。

反向代理的主要作用如下:

(1)保证内网的安全。可以使用反向代理提供WAF功能,阻止web攻击(大型网站通常将反向代理作为公网访问地址,将Web服务器作为内网)。

 

(2)负载均衡。当单机无法支撑一个网站应用时,就要考虑使用多台机器横向扩展的方式来处理多个请求,把请求分发到多台机器上的技术就是负载均衡。

Nginx使用upstream来定义一组参与负载均衡的服务器(可以在nginx.conf的http段中配置,默认路径:/usr/local/nginx/conf/),我介绍一个简单的配置:

上述配置定义了三个服务器,然后在server配置段中使用proxy_pass来定义使用的服务器组,就非常容易的将1024do.com这个站点配置成了负载均衡的。上述的配置默认是按顺序轮询,因服务器的所处位置、硬件性能等可以配置的更灵活。

Nginx的负载均衡可以划分为两大类:内置策略和扩展策略。内置策略包含加权轮询和IP hash,在默认情况下会编译进Nginx内核,只需在Nginx配置中指明参数。扩展策略有第三方模块策略:fair、url hash等。下面主要分析一下内置策略:

(1)加权轮询策略

在上述的服务器列表后面加上weight参数来设置权重,数字越大权重越大,分配的请求就越多。加权轮询策略不依赖于客户端的任何信息,完全依靠后端服务器的情况来进行选择,但是同一个客户端的多次请求可能会被分配到不同的后端服务器进行处理,无法满足做会话保持的需求。

需要注意的是,Nginx每次选出的服务器并不一定是当前权重最大的,整体上是根据服务器的权重在各个服务器上按照比例分布的。

(2)IP Hash策略

这种轮询策略是将请求ip和服务器建立起稳固的关系,每个请求按访问ip进行hash分配,这样每个client会固定访问一个后端服务器。因此IP Hash可以轻松的解决负载均衡时单机session变化的问题。

IP Hash虽然解决了会话保持的需求,但是如果hash后的结果拥挤在一台服务器上时,将导致某台服务器的压力非常大。如果仅仅为了会话保持,可以考虑将session迁移至数据库。

关于负载均衡服务器的主要配置参数如下:

1)自定义端口

2)使用down参数指定服务器不参与分发请求

3)backup指定候补服务器

正常情况下不会使用候补服务器,只有后端服务器比较繁忙或压力大时才会使用。

4)max_fairs审核服务器的健康状况

max_fairs可以设定一个请求失败的次数,超过限度则被认为服务器不可用,不再分发请求到此服务器上。

Nginx的epoll模型

谈及epoll就不得不提select、poll两种事件驱动,鉴于篇幅,感兴趣的可以查阅我之前博客:select 、poll 。

Nginx的诞生主要是为了解决C10k问题,这与它设计之初的架构是分不开的。在Linux早期很长一段时间内都是使用select来监听事件的,直到Linux2.6内核才提出了epoll,它也是Nginx之所以高并发、高性能的核心。

epoll不是使用一个函数,而是使用C库封装的3个接口:

epoll如何巧妙利用上述三个接口在User Space和Kernel Space中提高并发与性能?我们来看一张图:

上图未对具体操作说明,下面我补充一下:

步骤一:首先执行eoll_create在内核维护一块epoll的高速cache区,并在该缓冲区建立红黑树和就绪链表,用户传入的文件句柄将被放到红黑树中(这也是第一次拷贝)。

步骤二:内核针对读缓冲区和写缓冲区来判断是否可读可写,这个动作与epoll无关。

步骤三:epoll_ctl执行EPOLL_CTL_ADD动作时除了将文件句柄挂在红黑树上之外,还向内核注册了该文件句柄的回调函数,内核在检测到某句柄可读可写时则调用该回调函数,回调函数将文件句柄挂到就绪链表上。

步骤四:epoll_wait负责监控就绪链表,如果就绪链表存在文件句柄,则表示该文件句柄可读可写,则返回到用户态(少量的二次拷贝)。

步骤五:由于内核不修改文件句柄的位,因此只需要在第一次传入时就可以重复监控,直到使用epoll_ctl删除,否则不需要重新传入,因此无需多次拷贝。

简单来说,epoll是继承了select、poll的I/O多路复用的思想,并在二者的基础上从监控I/O流,查找I/O事件等角度来提高效率,从内核句柄列表到红黑书,再到就绪链表来实现的。

总结一下epoll的优点如下:

(1)监视的fd数量不受限制。它所支持的fd上限是最大能打开文件的数目,受限于内存大小。具体数目可以在/proc/sys/fs/file-max目录下查看。

(2)IO的效率不会随着监视fd的数量增多而下降。epoll不同于select和poll的轮询方式,而是通过每个fd定义的回调函数来实现的,只有就绪的fd才会执行回调函数。

(3)mmap加速内核与用户空间的信息传递。epoll是通过内核与用户空间内存映射的同一块内存,避免了无谓的内存拷贝。

(4)获取就绪事件的时间复杂度为O(1)。调用epoll_wait时,无需遍历,只要list中有数据就返回,没有数据就sleep,等到timeout后即使list有数据也返回。

(5)支持ET模式。下列图示并说明一下epoll的两种模式:

LT(level triggered):作为epoll缺省的工作模式,同时支持阻塞和非阻塞方式。LT模式下,内核告诉你一个文件描述符是否就绪了,然后你可以处理这个就绪事件,如果你不做任何操作,内核会继续通知你,直到事件被处理,在一定程序上降低了出错率。

ET(edge triggered):Nginx的默认工作模式,仅支持非阻塞方式。与LT的区别在于,当一个新的事件到来时,ET模式下可以从epoll_wait中获取到这个事件,应用程序应该立即处理该事件,因为epoll_wait后续调用将不再通知此事件(因此ET模式下缓冲区数据要一次性读干净,防止其他事件得不到处理)。ET模式在很大程度上降低了同一个事件被多次触发的可能,因此更加高效。

Keepalived实现Nginx高可用

Nginx作为入口网关,如果出现单点问题,显然是不可接受的,因此Keepalived应运而生(当然还有heartbeat,corosync等)。Keepalived作为一个高可用的解决方案,主要是用来防止服务器单点发生故障,可以通过和Nginx配合来实现Web服务的高可用。

Keepalived是以VRRP(Virtual Router Redundancy Protocol)协议来实现的,即虚拟路由冗余协议。它是将多台提供相同功能的路由器构成一个路由器组,这个组里面存在一个master和多个backup,master上有一个对外提供服务的虚拟ip,它会发组播给backup,当backup收不到VRRP包时就认为master宕掉了,需要根据VRRP的优先级来选举一个backup充当master。

VRRP的工作逻辑如下图:

介绍完VRRP之后,我们回到Keepalived实现Nginx的高可用上,它的实现思路主要包含两步:

① 请求不会直接打到Nginx上,会先通过虚拟IP;

② Keepalived可以监控Nginx的生命状态,提供一个用户自定义的脚本,来定期检查Nginx进程的状态,进行权重变化,从而实现Nginx故障切换。

简单来说,外界过来一个request请求会先通过VRRP得到虚拟IP,虚拟IP通过脚本来检测Nginx进程的健康状况,当Nginx1发生故障时,会将资源切换至备用的Nginx2上,从而体现Nginx的可高用性。

总结:本篇主要从Nginx的稳定性、高并发、高性能以及高可用四个方面进行了剖析,理解透这些相信你对Nginx会有更为清晰的认识,试着将这些原理运用于实际之中吧!

版权声明:本文著作权归原作者所有,欢迎分享本文,谢谢支持!
转载请注明:从“Nginx”的世界路过 | 术与道的分享
分类:操作系统 标签:, ,
1024do.com导航_术与道导航平台

发表评论


表情