Nginx面试题(史上最全 + 持续更新)
Posted 疯狂创客圈
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Nginx面试题(史上最全 + 持续更新)相关的知识,希望对你有一定的参考价值。
文章很长,而且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 :
免费赠送 :《尼恩Java面试宝典》 持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备
免费赠送 经典图书:《Java高并发核心编程(卷1)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷2)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《Java高并发核心编程(卷3)加强版》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 经典图书:《尼恩Java面试宝典》 面试必备 + 大厂必备 +涨薪必备 加尼恩免费领
免费赠送 资源宝库: Java 必备 百度网盘资源大合集 价值>10000元 加尼恩领取
Nginx的并发能力在同类型网页服务器中的表现,相对而言是比较好的,因此受到了很多企业的青睐,我国使用Nginx网站的知名用户包括腾讯、淘宝、百度、京东、新浪、网易等等。
Nginx是网页服务器运维人员必备技能之一,下面为大家整理了一些比较常见的Nginx相关面试题
聊聊:什么是Nginx?
Nginx是一个web服务器和反向代理服务器,用于HTTP、HTTPS、SMTP、POP3和IMAP协议。
Nginx—Ngine X,是一款免费的、自由的、开源的、高性能HTTP服务器和反向代理服务器;
也是一个IMAP、POP3、SMTP代理服务器;
Nginx以其高性能、稳定性、丰富的功能、简单的配置和低资源消耗而闻名。
也就是说Nginx本身就可以托管网站(类似于Tomcat一样),进行Http服务处理,也可以作为反向代理服务器 、负载均衡器和HTTP缓存。
Nginx 解决了服务器的C10K(就是在一秒之内连接客户端的数目为10k即1万)问题。
它的设计不像传统的服务器那样使用线程处理请求,而是一个更加高级的机制—事件驱动机制,是一种异步事件驱动结构。
聊聊:Nginx的一些特性。
Nginx服务器的特性包括:
- 反向代理/L7负载均衡器
- 嵌入式Perl解释器
- 动态二进制升级
- 可用于重新编写URL,具有非常好的PCRE支持
聊聊:Nginx的优缺点?
核心优点:
- 占内存小,可实现高并发连接,处理响应快
- 可实现http服务器、虚拟主机、方向代理、负载均衡
- Nginx配置简单
- 可以不暴露正式的服务器IP地址
核心缺点:
- 动态处理差:
nginx处理静态文件好,耗费内存少,但是处理动态页面则很鸡肋,
现在一般前端用nginx作为反向代理抗住压力。
聊聊:Nginx应用场景?
- http服务器。
Nginx是一个http服务可以独立提供http服务。可以做网页静态服务器。
- 虚拟主机。
可以实现在一台服务器虚拟出多个网站,例如个人网站使用的虚拟机。
- 反向代理,负载均衡。
当网站的访问量达到一定程度后,单台服务器不能满足用户的请求时,需要用多台服务器集群可以使用nginx做反向代理。
并且多台服务器可以平均分担负载,不会应为某台服务器负载高宕机而某台服务器闲置的情况。
nginz 中也可以配置安全管理、比如可以使用Nginx搭建API接口网关,对每个接口服务进行拦截。
聊聊:使用“反向代理服务器”的优点是什么?
反向代理服务器可以隐藏源服务器的存在和特征。
它充当互联网云和web服务器之间的中间层。
这对于安全方面来说是很好的,特别是当您使用web托管服务时。
聊聊:什么是正向代理和反向代理?
首先,代理服务器一般指局域网内部的机器通过代理服务器发送请求到互联网上的服务器,代理服务器一般作用在客户端。例如:GoAgentFQ软件。
我们的客户端在进行FQ操作的时候,我们使用的正是正向代理,通过正向代理的方式,在我们的客户端运行一个软件,将我们的HTTP请求转发到其他不同的服务器端,实现请求的分发。
反向代理服务器作用在服务器端,它在服务器端接收客户端的请求,然后将请求分发给具体的服务器进行处理,然后再将服务器的相应结果反馈给客户端。Nginx就是一个反向代理服务器软件。
从上图可以看出:客户端必须设置正向代理服务器,当然前提是要知道正向代理服务器的IP地址,还有代理程序的端口。
反向代理正好与正向代理相反,对于客户端而言代理服务器就像是原始服务器,并且客户端不需要进行任何特别的设置。客户端向反向代理的命名空间(name-space)中的内容发送普通请求,接着反向代理将判断向何处(原始服务器)转交请求,并将获得的内容返回给客户端。
聊聊:反向代理好处
- 保护了真实的 web 服务器,web 服务器对外不可见,外网只能看到反向代理服务器,而反向代理服务器上并没有真实数据,因此,保证了 web 服务器的资源安全。
- 反向代理为基础产生了动静资源分离以及负载均衡的方式,减轻 web 服务器的负担,加速了对网站访问速度。
- 节约了有限的 IP 地址资源,企业内所有的网站共享一个在 internet 中注册的IP地址,这些服务器分配私有地址,采用虚拟主机的方式对外提供服务。
聊聊:什么是Nginx? 它的优势和功能?
Nginx是一个web服务器和方向代理服务器,用于HTTP、HTTPS、SMTP、POP3和IMAP协议。因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。
优点:
(1)更快
这表现在两个方面:一方面,在正常情况下,单次请求会得到更快的响应;另一方面,在高峰期(如有数以万计的并发请求),Nginx可以比其他Web服务器更快地响应请求。
(2)高扩展性,跨平台
Nginx的设计极具扩展性,它完全是由多个不同功能、不同层次、不同类型且耦合度极低的模块组成。因此,当对某一个模块修复Bug或进行升级时,可以专注于模块自身,无须在意其他。而且在HTTP模块中,还设计了HTTP过滤器模块:一个正常的HTTP模块在处理完请求后,会有一串HTTP过滤器模块对请求的结果进行再处理。这样,当我们开发一个新的HTTP模块时,不但可以使用诸如HTTP核心模块、events模块、log模块等不同层次或者不同类型的模块,还可以原封不动地复用大量已有的HTTP过滤器模块。这种低耦合度的优秀设计,造就了Nginx庞大的第三方模块,当然,公开的第三方模块也如官方发布的模块一样容易使用。
Nginx的模块都是嵌入到二进制文件中执行的,无论官方发布的模块还是第三方模块都是如此。这使得第三方模块一样具备极其优秀的性能,充分利用Nginx的高并发特性,因此,许多高流量的网站都倾向于开发符合自己业务特性的定制模块。
(3)高可靠性:用于反向代理,宕机的概率微乎其微
高可靠性是我们选择Nginx的最基本条件,因为Nginx的可靠性是大家有目共睹的,很多家高流量网站都在核心服务器上大规模使用Nginx。Nginx的高可靠性来自于其核心框架代码的优秀设计、模块设计的简单性;另外,官方提供的常用模块都非常稳定,每个worker进程相对独立,master进程在1个worker进程出错时可以快速“拉起”新的worker子进程提供服务。
(4)低内存消耗
一般情况下,10 000个非活跃的HTTP Keep-Alive连接在Nginx中仅消耗2.5MB的内存,这是Nginx支持高并发连接的基础。
(5)单机支持10万以上的并发连接
这是一个非常重要的特性!
随着互联网的迅猛发展和互联网用户数量的成倍增长,各大公司、网站都需要应付海量并发请求,一个能够在峰值期顶住10万以上并发请求的Server,无疑会得到大家的青睐。
理论上,Nginx支持的并发连接上限取决于内存,10万远未封顶。当然,能够及时地处理更多的并发请求,是与业务特点紧密相关的。
(6)热部署
master管理进程与worker工作进程的分离设计,使得Nginx能够提供热部署功能,即可以在7×24小时不间断服务的前提下,升级Nginx的可执行文件。
当然,它也支持不停止服务就更新配置项、更换日志文件等功能。
(7)最自由的BSD许可协议
这是Nginx可以快速发展的强大动力。
BSD许可协议不只是允许用户免费使用Nginx,它还允许用户在自己的项目中直接使用或修改Nginx源码,然后发布。这吸引了无数开发者继续为Nginx贡献自己的智慧。
以上7个特点当然不是Nginx的全部,拥有无数个官方功能模块、第三方功能模块使得Nginx能够满足绝大部分应用场景,这些功能模块间可以叠加以实现更加强大、复杂的功能,有些模块还支持Nginx与Perl、Lua等脚本语言集成工作,大大提高了开发效率。这些特点促使用户在寻找一个Web服务器时更多考虑Nginx。
选择Nginx的核心理由还是它能在支持高并发请求的同时保持高效的服务
聊聊:为什么要用Nginx?
- 跨平台、配置简单、方向代理、高并发连接:处理2-3万并发连接数,官方监测能支持5万并发,内存消耗小:开启10个nginx才占150M内存 ,nginx处理静态文件好,耗费内存少,
- 而且Nginx内置的健康检查功能:如果有一个服务器宕机,会做一个健康检查,再发送的请求就不会发送到宕机的服务器了。重新将请求提交到其他的节点上。
- 使用Nginx的话还能:
- 节省宽带:支持GZIP压缩,可以添加浏览器本地缓存
- 稳定性高:宕机的概率非常小
- 接收用户请求是异步的
聊聊:请解释什么是C10K问题?
C10K问题是指无法同时处理大量客户端(10,000)的网络套接字。
聊聊:C10K问题的本质和解决方案
什么是C10K问题
所谓 c10k 问题,指的是服务器如何支持 10k 个并发连接,也就是 concurrent 10000 connection(这也是 c10k 这个名字的由来)。
由于硬件成本的大幅度降低和硬件技术的进步,如果一台服务器能够同时服务更多的客户端,那么也就意味着服务每一个客户端的成本大幅度降低。从这个角度来看,c10k 问题显得非常有意义。
C10K问题由来
互联网的基础是网络通信,早期的互联网可以说是一个小群体的集合。互联网还不够普及,用户也不多,一台服务器同时在线 100 个用户,在当时已经算是大型应用了,所以并不存在 C10K 的难题。互联网的爆发期是在 www 网站、浏览器出现后。最早的互联网称之为 Web1.0,大部分的使用场景是下载一个 HTML 页面,用户在浏览器中查看网页上的信息,这个时期也不存在 C10K 问题。
Web2.0 时代到来后,就不同了。一方面是,互联网普及率大大提高了,用户群体几何倍增长。另一方面是,互联网不再是单纯地浏览 www 网页,逐渐开始进行交互,而且应用程序的逻辑也变得更复杂。从简单的表单提交,到即时通信和在线实时互动,C10K 的问题才体现出来了。因为每一个用户都必须与服务器保持连接,才能进行实时数据交互。诸如 Facebook 这样的网站,同一时间的并发 TCP 连接很可能已经过亿。
早期的腾讯QQ也同样面临C10K问题,只不过他们是用了UDP这种原始的包交换协议来实现的,绕开了这个难题,当然过程肯定是痛苦的。如果当时有 epoll 技术,他们肯定会用 TCP。众所周之,后来的手机 QQ、微信都采用 TCP 协议。
实际上,当时也有异步模式,如:select/poll 模型。这些技术都有一定的缺点:selelct 最大不能超过 1024;poll 没有限制,但每次收到数据时,需要遍历每一个连接,查看哪个连接有数据请求。
这时候问题就来了,最初的服务器都是基于进程/线程模型的,新到来一个 TCP 连接,就需要分配 1 个进程(或者线程)。进程又是操作系统最昂贵的资源,一台机器无法创建很多进程。如果是 C10K,就要创建 1 万个进程,那么就单机而言,操作系统是无法承受的(往往出现效率低下、甚至完全瘫痪)。如果是采用分布式系统,维持 1 亿用户在线需要 10 万台服务器,成本巨大,也只有 Facebook、Google、Apple 等巨头,才有财力购买如此多的服务器。
基于上述考虑,如何突破单机性能局限,是高性能网络编程所必须要直面的问题。这些局限和问题,最早被 Dan Kegel 进行了归纳和总结,并首次系统地分析和提出了解决方案。后来,这种普遍的网络现象和技术局限,都被大家称为 C10K 问题。
C10K问题的本质
C10K 问题,本质上是操作系统的问题。对于 Web1.0/2.0 时代的操作系统而言,传统的同步阻塞 I/O 模型都是一样的,处理的方式都是 requests per second,并发 10K 和 100 的区别关键在于 CPU。
创建的进程、线程多了,数据拷贝频繁(缓存 I/O、内核将数据拷贝到用户进程空间、阻塞), 进程/线程上下文切换消耗大, 导致操作系统崩溃,这就是 C10K 问题的本质!
可见,解决 C10K 问题的关键就是:尽可能减少 CPU 等核心资源消耗,从而榨干单台服务器的性能,突破 C10K 问题所描述的瓶颈。
C10K问题的解决方案探讨
从网络编程技术的角度来说,主要思路为:
- 为每个连接分配一个独立的线程/进程。
- 同一个线程/进程同时处理多个连接(IO 多路复用)。
为每个连接分配一个独立的线程/进程
这一思路最为直接。但是,由于申请进程/线程会占用相当可观的系统资源,同时对于多进程/线程的管理会对系统造成压力,因此,这种方案不具备良好的可扩展性。
这一思路在服务器资源还没有富裕到足够程度的时候,是不可行的。即便资源足够富裕,效率也不够高。
总之,此思路技术实现会使得资源占用过多,可扩展性差,在实际应用中已被抛弃。
同一个线程/进程同时处理多个连接(IO多路复用)
IO 多路复用,从技术实现上,又分很多种。我们逐一来看看下述各种实现方式的优劣。
实现方式1:循环逐个处理各个连接,每个连接对应一个 socket
循环逐个处理各个连接,每个连接对应一个 socket。当所有 socket 都有数据的时候,这种方法是可行的。但是,当应用读取某个 socket 的文件数据不 ready 的时候,整个应用会阻塞在这里,等待该文件句柄 ready,即使别的文件句柄 ready,也无法往下处理。
实现小结:直接循环处理多个连接。
问题归纳:任一文件句柄的不成功会阻塞住整个应用。
实现方式2:使用 select 方法
使用 select 方法解决上面阻塞的问题,思路比较简单。在读取文件句柄之前,先查下它的状态,如果 ready 了,就进行处理;如果不 ready, 就不进行处理;这不就解决了这个问题了嘛?于是,有了 select 方案。用一个 fd_set 结构体来告诉内核同时监控多个文件句柄,当其中有文件句柄的状态发生指定变化(例如某句柄由不可用变为可用)或超时,则调用返回。
之后,应用可以使用 FD_ISSET 来逐个查看,确定哪个文件句柄的状态发生了变化。这样做,小规模的连接问题不大,但当连接数很多(文件句柄个数很多)的时候,逐个检查状态就很慢了。因此,select 往往存在管理的句柄上限(FD_SETSIZE)。同时,在使用上,因为只有一个字段记录关注和发生事件,所以每次调用之前,要重新初始化 fd_set 结构体。
intselect(intnfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,structtimeval *timeout);
实现小结:有连接请求抵达了,再检查处理。
问题归纳:句柄上限+重复初始化+逐个排查所有文件句柄状态,效率不高。
实现方式3:使用 poll 方法
poll 主要解决 select 的前两个问题:
- 通过一个 pollfd 数组,向内核传递需要关注的事件,以消除文件句柄上限。
- 使用不同字段分别标注 “关注事件和发生事件”,来避免重复初始化。
实现小结:设计新的数据结构,提高使用效率。
问题归纳:逐个排查所有文件句柄状态,效率不高。
实现方式4:使用 epoll 方法
既然 “poll 逐个排查所有文件句柄状态” 效率不高,很自然的,在调用返回的时候,如果只给应用提供发生了状态变化(很可能是数据 ready)的文件句柄,进行排查的效率就高很多。epoll 采用了这种设计,适用于大规模的应用场景。实验表明:当文件句柄数目超过10之后,epoll 性能将优于 select 和 poll;当文件句柄数目达到 10K 的时候,epoll 已经超过 select 和 poll 两个数量级。
实现小结:只返回状态变化的文件句柄。
问题归纳:依赖特定平台(Linux)。
因为 Linux 是互联网企业中使用率最高的操作系统,所以 Epoll 就成为 “C10K killer、高并发、高性能、异步非阻塞” 这些技术的代名词了。FreeBSD 推出了 kqueue,Linux 推出了 epoll,Windows 推出了 IOCP,Solaris 推出了 /dev/poll。这些操作系统提供的功能,就是为了解决 C10K 问题。epoll 技术的编程模型就是异步非阻塞回调,也可以叫做 Reactor、事件驱动、事件轮循(EventLoop)。Nginx、libevent、node.js 这些就是 Epoll 时代的产物。
实现方式5:使用 libevent 库
由于 epoll,、kqueue、IOCP 每个接口都有自己的特点,程序移植非常困难,所以需要对这些接口进行封装,以让它们易于使用和移植,其中 libevent 库就是其中之一。跨平台,封装底层平台的调用,提供统一的 API,但底层在不同平台上自动选择合适的调用。
按照 libevent 的官方网站,libevent 库提供了以下功能:当一个文件描述符的特定事件(如可读,可写或出错)发生了,或一个定时事件发生了,libevent 就会自动执行用户指定的回调函数,来处理事件。目前,libevent 已支持以下接口 /dev/poll、kqueue、event ports、select、poll 和 epoll。Libevent 的内部事件机制完全是基于所使用的接口的。因此,libevent 非常容易移植,也使它的扩展性非常容易。目前,libevent 已在以下操作系统中编译通过:Linux、BSD、Mac OS X、Solaris 和 Windows。使用 libevent 库进行开发非常简单,也很容易在各种 unix 平台上移植。
聊聊:Nginx如何实现超高并发?
面试官心理分析
主要是看应聘人员的对NGINX的基本原理是否熟悉,因为大多数人多多少少都懂点NGINX,但是真正其明白原理的可能少之又少。
明白其原理,才能做优化,否则只能照样搬样,出了问题也无从下手。
懂皮毛的人,一般会做个 Web Server,搭建一个 Web 站点;
初级运维可能搞个 HTTPS 、配置一个反向代理; 中级运维定义个 upstream、写个正则判断;
老鸟做个性能优化、写个ACL,还有可能改改源码。
面试题剖析
异步,非阻塞,使用了epoll 和大量的底层代码优化。
如果一个server采用一个进程负责一个request的方式,那么进程数就是并发数。正常情况下,会有很多进程一直在等待中。
而nginx采用一个master进程,多个woker进程的模式。
- master进程主要负责收集、分发请求。每当一个请求过来时,master就拉起一个worker进程负责处理这个请求。
- 同时master进程也负责监控woker的状态,保证高可靠性
- woker进程一般设置为跟cpu核心数一致。nginx的woker进程在同一时间可以处理的请求数只受内存限制,可以处理多个请求。
- Nginx 的异步非阻塞工作方式正把当中的等待时间利用起来了。在需要等待的时候,这些进程就空闲出来待命了,因此表现为少数几个进程就解决了大量的并发问题。
每进来一个request,会有一个worker进程去处理。但不是全程的处理,处理到什么程度呢?处理到可能发生阻塞的地方,比如向上游(后端)服务器转发request,并等待请求返回。那么,这个处理的worker很聪明,他会在发送完请求后,注册一个事件:“如果upstream返回了,告诉我一声,我再接着干”。于是他就休息去了。
此时,如果再有request 进来,他就可以很快再按这种方式处理。而一旦上游服务器返回了,就会触发这个事件,worker才会来接手,这个request才会接着往下走。
聊聊:Nginx如何实现超高并发?
Nginx 是一个高性能的 Web 服务器,能够同时处理大量的并发请求。
它结合多进程机制和异步机制 ,异步机制使用的是异步非阻塞方式,
接下来就给大家介绍一下 Nginx 的多线程机制和异步非阻塞机制。
1、多进程机制
服务器每当收到一个客户端时,就有 服务器主进程 ( master process )生成一个 子进程( worker process )出来和客户端建立连接进行交互,直到连接断开,该子进程就结束了。
使用进程的好处是各个进程之间相互独立,不需要加锁,减少了使用锁对性能造成影响,同时降低编程的复杂度,降低开发成本。其次,采用独立的进程,可以让进程互相之间不会影响 ,如果一个进程发生异常退出时,其它进程正常工作, master 进程则很快启动新的 worker 进程,确保服务不会中断,从而将风险降到最低。
缺点是操作系统生成一个子进程需要进行 内存复制等操作,在资源和时间上会产生一定的开销。当有大量请求时,会导致系统性能下降 。
2、异步非阻塞机制
每个工作进程 使用 异步非阻塞方式 ,可以处理 多个客户端请求 。
当某个 工作进程 接收到客户端的请求以后,调用 IO 进行处理,如果不能立即得到结果,就去 处理其他请求 (即为 非阻塞 );而 客户端 在此期间也 无需等待响应 ,可以去处理其他事情(即为 异步 )。
当 IO 返回时,就会通知此 工作进程 ;该进程得到通知,暂时 挂起 当前处理的事务去 响应客户端请求 。
聊聊:请解释Nginx服务器上的Master和Worker进程分别是什么?
- 主程序 Master process 启动后,通过一个 for 循环来 接收 和 处理外部信号 ;
- 主进程通过 fork() 函数产生 worker 子进程 ,每个子进程执行一个 for循环来实现Nginx服务器对事件的接收和处理 。
一般推荐 worker 进程数与CPU内核数一致,这样一来不存在大量的子进程生成和管理任务,避免了进程之间竞争CPU 资源和进程切换的开销。而且 Nginx 为了更好的利用 多核特性 ,提供了 CPU 亲缘性的绑定选项,我们可以将某一个进程绑定在某一个核上,这样就不会因为进程的切换带来 Cache 的失效。
对于每个请求,有且只有一个工作进程 对其处理。首先,每个 worker 进程都是从 master进程 fork 过来。在 master 进程里面,先建立好需要 listen 的 socket(listenfd) 之后,然后再 fork 出多个 worker 进程。
所有 worker 进程的 listenfd 会在新连接到来时变得可读 ,为保证只有一个进程处理该连接,所有 worker 进程在注册 listenfd 读事件前抢占 accept_mutex ,抢到互斥锁的那个进程注册 listenfd 读事件 ,在读事件里调用 accept 接受该连接。
当一个 worker 进程在 accept 这个连接之后,就开始读取请求、解析请求、处理请求,产生数据后,再返回给客户端 ,最后才断开连接。这样一个完整的请求就是这样的了。我们可以看到,一个请求,完全由 worker 进程来处理,而且只在一个 worker 进程中处理。
在 Nginx 服务器的运行过程中, 主进程和工作进程 需要进程交互。交互依赖于 Socket 实现的管道来实现。
聊聊:请列举Nginx服务器的最佳用途。
Nginx服务器的最佳用法是在网络上部署动态HTTP内容,使用SCGI、WSGI应用程序服务器、用于脚本的FastCGI处理程序。
它还可以作为负载均衡器。
聊聊:Nginx负载均衡实现过程
首先在http模块中配置使用upstream模块定义后台的webserver的池子,并且给池子命名, 比如命名为proxy-web,
在池子中我们可以添加多台后台webserver,其中状态检查、调度算法都是在池子中配置;
然后在server模块中定义虚拟 location 地址,但是这个虚拟location 地址 不指定自己的web目录站点,
它将使用location 匹配url然后转发到上面定义好的web池子中,
后台的webserver的池子,最后根据调度策略再转发到后台web server上。
聊聊:Nginx负载均衡配置
Upstream proxy_nginx
server 192.168.0.254 weight=1max_fails=2 fail_timeout=10s ;
server 192.168.0.253 weight=2 max_fails=2fail_timeout=10s;
server192.168.0.252 backup; server192.168.0.251 down;
server
listen 80;
server_name xiaoka.com;
location /
proxy_pass http:// proxy_nginx;
proxy_set_header Host
proxy_set_header X-Real-IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
聊聊:nginx的常用的负载均衡算法?
1、round-robin
round-robin的意思是循环轮询。
Nginx最简单的负载均衡配置如下
http
upstream app1
server 10.10.10.1;
server 10.10.10.2;
server
listen 80;
location /
proxy_pass http://app1;
upstream app1用来指定一个服务器组,该组的名字是app1,包含两台服务器。在指定服务器组里面包含的服务器时以形式“server ip/domain:port”的形式指定,其中80端口可以忽略。然后在接收到请求时通过“proxy_pass http://app1”把对应的请求转发到组app1上。Nginx默认的负载均衡算法就是循环轮询,如上配置我们采用的就是循环轮询,其会把接收到的请求循环的分发给其包含的(当前可用的)服务器。使用如上配置时,Nginx会把第1个请求给10.10.10.1,把第2个请求给10.10.10.2,第3个请求给10.10.10.1,以此类推。
2、least-connected
least-connected算法的中文翻译是最少连接,即每次都找连接数最少的服务器来转发请求。例如Nginx负载中有两台服务器,A和B,当Nginx接收到一个请求时,A正在处理的请求数是10,B正在处理的请求数是20,则Nginx会把当前请求交给A来处理。要启用最少连接负载算法只需要在定义服务器组时加上“least_conn”,如:
upstream app1
least_conn;
server 10.10.10.1;
server 10.10.10.2;
3、ip-hash
ip-hash算法会根据请求的客户端IP地址来决定当前请求应该交给谁。使用ip-hash算法时Nginx会确保来自同一客户端的请求都分发到同一服务器。要使用ip-hash算法时只需要在定义服务器组时加上“ip-hash ”指令,如:
upstream app1
ip_hash;
server 10.10.10.1;
server 10.10.10.2;
4、weighted
weighted算法也就是权重算法,会根据每个服务的权重来分发请求,权重大的请求相对会多分发一点,权重小的会少分发一点。这通常应用于多个服务器的性能不一致时。需要使用权重算法时只需要在定义服务器组时在服务器后面指定参数weight,如:
upstream app1
server 10.10.10.1 weight=3;
server 10.10.10.2;
聊聊:Nginx 有哪些负载均衡策略
Nginx 默认提供的负载均衡策略:
1、轮询(默认)round_robin
每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器 down 掉,能自动剔除。
2、IP 哈希 ip_hash
每个请求按访问 ip 的 hash 结果分配,这样每个访客固定访问一个后端服务器,可以解决 session 共享的问题。
当然,实际场景下,一般不考虑使用 ip_hash 解决 session 共享。
3、最少连接 least_conn
下一个请求将被分派到活动连接数量最少的服务器
4、权重 weight
weight的值越大分配到的访问概率越高,主要用于后端每台服务器性能不均衡的情况下,达到合理的资源利用率。 还可以通过插件支持其他策略。
聊聊:ngx_http_upstream_module的作用是什么?
ngx_http_upstream_module用于定义可通过fastcgi传递、proxy传递、uwsgi传递、memcached传递和scgi传递指令来引用的服务器组。
聊聊:Nginx配置高可用性怎么配置?
- 当上游服务器(真实访问服务器),一旦出现故障或者是没有及时相应的话,应该直接轮训到下一台服务器,保证服务器的高可用
- Nginx配置代码:
server
listen 80;
server_name www.lijie.com;
location /
### 指定上游服务器负载均衡服务器
proxy_pass http://backServer;
###nginx与上游服务器(真实访问的服务器)超时时间 后端服务器连接的超时时间_发起握手等候响应超时时间
proxy_connect_timeout 1s;
###nginx发送给上游服务器(真实访问的服务器)超时时间
proxy_send_timeout 1s;
### nginx接受上游服务器(真实访问的服务器)超时时间
proxy_read_timeout 1s;
index index.html index.htm;
聊聊:Nginx为什么不使用多线程?
apache:
创建多个进程或线程,而每个进程或线程都会为其分配cpu和内存(线程要比进程小的多,所以worker支持比perfork高的并发),并发过大会榨干服务器资源。
Nginx:
采用单线程来异步非阻塞处理请求(管理员可以配置Nginx主进程的工作进程的数量)(epoll),不会为每个请求分配cpu和内存资源,节省了大量资源,同时也减少了大量的CPU的上下文切换。
所以才使得Nginx支持更高的并发。
聊聊:Nginx为什么不使用多线程?
答:
Nginx:
采用单线程来异步非阻塞处理请求(管理员可以配置Nginx主进程的工作进程的数量),
不会为每个请求分配cpu和内存资源,节省了大量资源,同时也减少了大量的CPU的上下文切换,所以才使得Nginx支持更高的并发。
聊聊:Nginx主要特性
- 支持SSL 和TLSSNI.
Nginx它支持内核Poll模型,能经受高负载的考验,有报告表明能支持高达50,000个并发连接数。 - Nginx具有很高的稳定性。
例如当前 apache一旦上到200个以上进程,web响应速度就明显非常缓慢了。而Nginx采取了分阶段资源分配技术,使得它的CPU与内存占用率非常低。nginx官方表示保持10,000个没有活动的连接,它只占2.5M内存,所以类似DOS这样的攻击对nginx来说基本上是毫无用处的。 - Nginx支持热部署。
它的启动特别容易,并且几乎可以做到7*24不间断运行,即使运行数个月也不需要重新启动。对软件版本进行进行热升级。 - Nginx采用master-slave模型,能够充分利用SMP的优势,且能够减少工作进程在磁盘IO的阻塞延迟。
当采用select()/poll()调用时,还可以限制每个进程的连接数。 - Nginx采用master-slave模型,能够充分利用SMP的优势,且能够减少工作进程在磁盘IO的阻塞延迟。
当采用select()/poll()调用时,还可以限制每个进程的连接数。 - Nginx采用了一些os提供的最新特性如对sendfile (Linux2.2+), accept-filter
(FreeBSD4.1+),TCP_DEFER_ACCEPT (Linux 2.4+)的支持,从而大大提高了性能。 - 免费开源,可以做高并发负载均衡。
聊聊:Nginx常用命令
1、启动
nginx
2、停止
nginx -s stop
或
nginx -s quit
3、重载配置
./sbin/nginx -s reload(平滑重启 )
或
service nginx reload
4、重载指定配置文件
.nginx -c /usr/local/nginx/conf/nginx.conf
5、查看版本
nginx -v
6、检查配置文件是否正确
nginx -t
7、显示帮助命令
nginx -h
聊聊:Nginx IO事件模型以及连接数上限
events
use epoll; #epoll 是多路复⽤ IO(I/O Multiplexing)中的⼀种⽅式,但是仅⽤于 linux2.6
#以上内核,可以⼤⼤提⾼ nginx 的性能
worker_connections 1024;#单个后台 worker process 进程的最⼤并发链接数
# multi_accept on;
聊聊:动态资源、静态资源分离的原因
动态资源、静态资源分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,
动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作,这就是网站静态化处理的核心思路
动态资源、静态资源分离简单的概括是:动态文件与静态文件的分离
二者分离的原因
在我们的软件开发中,
- 有些请求是需要后台处理的(如:.jsp,.do等等),
- 有些请求是不需要经过后台处理的(如:css、html、jpg、js等等文件)
这些不需要经过后台处理的文件称为静态文件,否则动态文件。
因此我们后台处理忽略静态文件。这会有人又说那我后台忽略静态文件不就完了吗
当然这是可以的,但是这样后台的请求次数就明显增多了。
在我们对资源的响应速度有要求的时候,我们应该使用这种动静分离的策略去解决
动、静分离将网站静态资源(HTML,JavaScript,CSS,img等文件)与后台应用分开部署,提高用户访问静态代码的速度,降低对后台应用访问
这里我们将静态资源放到nginx中,动态资源转发到tomcat服务器中
聊聊:Nginx怎么做的动静分离?
只需要指定路径对应的目录。
location/可以使用正则表达式匹配。并指定对应的硬盘中的目录。如下:
location /image/
root /usr/local/static/;
autoindex on;
创建目录
mkdir /usr/local/static/image
进入目录
cd /usr/local/static/image
放一张照片上去
1.jpg
重启 nginx
sudo nginx -s reload
打开浏览器 输入 server_name/image/1.jpg 就可以访问该静态图片了
聊聊:什么是动静分离?
动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作,这就是网站静态化处理的核心思路,实际上,何谓动?何谓静呢?拿我们 Java 来说 jsp、servlet 等就是动,因为他们离开我们的 web 服务器的支持就会无法正常工作。而 js、css 等文件就是静了。因为离开 web 服务器他一样能正常的工作。
动静分离简单的概括是:动态文件与静态文件的分离。
聊聊:Nginx动静态资源分离做过吗,为什么要这样做?
动态资源、静态资源分离,是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来。
比如说 js、css、hrml从A服务器返回。图片从B服务器返回,其他请求从Tomcat服务器C返回。
后台应用分开部署,提高用户访问静态代码的速度。而且现在还有CDN服务,不需要限制于服务器的带宽。
聊聊:Nginx如何做动静分离
Nginx 根据客户端请求的 url 来判断请求的是否是静态资源,如果请求的 url 包含 jpg、png,则由 Nginx 处理。如果请求的 url 是 .php 或者 .jsp 等等,这个时候这个请求是动态的,将转发给 tomcat 处理。
总结来说,Nginx 是通过 url 来区分请求的类型,并转发给不同的服务端。
聊聊:Nginx动静分离的好处
- api 接口服务化:动静分离之后,后端应用更为服务化,只需要通过提供 api 接口即可,可以为多个功能模块甚至是多个平台的功能使用,可以有效的节省后端人力,更便于功能维护。
- 前后端开发并行:前后端只需要关心接口协议即可,各自的开发相互不干扰,并行开发,并行自测,可以有效的提高开发时间,也可以有些的减少联调时间
- 减轻后端服务器压力,提高静态资源访问速度:后端不用再将模板渲染为 html 返回给用户端,且静态服务器可以采用更为专业的技术提高静态资源的访问速度。
聊聊:Nginx 和 Apache、Tomcat 之间的不同点
Tomcat 和Nginx/Apache区别:
1、Nginx/Apache 是Web Server,而Apache Tomact是一个servlet container
2、tomcat可以对jsp进行解析,nginx和apache只是web服务器,可以简单理解为只能提供html静态文件服务。
Nginx和Apache区别:
1)Nginx轻量级,同样起web 服务,比apache占用更少的内存及资源 。
2)Nginx 抗并发,nginx 处理请求是异步非阻塞的,而apache 则是阻塞型的,在高并发下nginx 能保持低资源低消耗高性能 。
3)Nginx提供负载均衡,可以做做反向代理,前端服务器
4)Nginx多进程单线程,异步非阻塞;Apache多进程同步,阻塞。
聊聊:Nginx和Apache 之间的不同点
nginx | apache |
---|---|
1.nginx 是一个基于web服务器 | 1.Apache 是一个基于流程的服务器 |
2.所有请求都由一个线程来处理 | 2.单线程处理单个请求 |
3.nginx避免子进程的概念 | 3.apache是基于子进程的 |
4.nginx类似于速度 | 4.apache类似于功率 |
5.nginx在内存消耗和连接方面比较好 | 5.apache在内存消耗和连接方面并没有提高 |
6.nginx在负载均衡方面表现较好 | 6.apache当流量达到进程的极限时,apache将拒绝新的连接 |
7.对于PHP来说,nginx更可取,因为他支持PHP | 7.apache支持的php python Perl和其他语言,使用插件,当应用程序基于python和ruby时,它非常有用 |
8.nginx 不支持像ibmi 和 openvms 一样的os | 8.apache支持更多的os |
9.nginx 只具有核心功能 | 9.apache 提供了比Nginx更多的功能 |
10.nginx 性能和可伸缩性不依赖于硬件 | 10.apache 依赖于CPU和内存等硬件组件 |
聊聊:Nginx与Apache对比
优点
- 轻量级,采用 C 语言 进行编写,同样的 web 服务,会占用更少的内存及资源。
- 抗并发,nginx 以 epoll and kqueue 作为开发模型,处理请求是异步非阻塞的,多个连接对应一个进程,负载能力比 apache 高很多,而 apache 则是同步多进程模型,只能一个连接对应一个进程,当压力过大时,它是会被阻塞型的。
在高并发下 nginx 能保持低资源低消耗高性能 ,而 apache 在 PHP 处理慢或者前端压力很大的情况下,很容易出现进程数飙升,从而拒绝服务的现象。 - 设计高度模块化,编写模块相对简单。
- 配置简洁,正则配置让很多事情变得简单,而且改完配置能使用 -t 测试配置有没有问题,apache 配置复杂 ,重启的时候发现配置出错了,会很崩溃。
- 一般用于处理静态文件,静态处理性能比 apache 高三倍以上。
- 作为负载均衡服务器,支持 7 层负载均衡。
- 本身就是一个反向代理服务器,而且可以作为非常优秀的邮件代理服务器。
- nginx 启动特别容易, 并且几乎可以做到 7*24 不间断运行,即使运行数个月也不需要重新启动,支持热部署,比如:实现不间断服务的情况下进行软件版本的升级与版本的回退。
- 社区活跃,各种高性能模块出品迅速。
缺点
- apache 的 rewrite 比 nginx 强大,在 rewrite 频繁的情况下,用 apache。
- apache 发展到现在,模块超多,基本想到的都可以找到。
- apache 更为成熟,少 bug ,nginx 的 bug 相对较多。
- apache 超稳定,Nginx 一个进程死掉时,会影响到多个用户的使用,稳定性差。
- apache 对 PHP 支持比较简单,nginx 需要配合其他后端用。
- apache 在处理动态请求有优势,nginx 在这方面是鸡肋,一般动态请求要 apache 去做,nginx 适合静态和反向。
- apache 仍然是目前的主流,拥有丰富的特性,成熟的技术和开发社区。
聊聊:Nginx与Apache选择
Apache
● apache 的 rewrite 比 nginx 强大,在 rewrite 频繁的情况下,用 apache
● apache 发展到现在,模块超多,基本想到的都可以找到
● apache 更为成熟,少 bug ,nginx 的 bug 相对较多
● apache 超稳定
● apache 对 PHP 支持比较简单,nginx 需要配合其他后端用
● apache 在处理动态请求有优势,nginx 在这方面是鸡肋,一般动态请求要 apache 去做,nginx 适合静态和反向。
● apache 仍然是目前的主流,拥有丰富的特性,成熟的技术和开发社区
Nginx
● 轻量级,采用 C 语言 进行编写,同样的 web 服务,会占用更少的内存及资源
● 抗并发,nginx 以 epoll and kqueue 作为开发模型,处理请求是异步非阻塞的,负载能力比 apache 高很多,而 apache 则是阻塞型的。在高并发下 nginx 能保持低资源低消耗高性能 ,而 apache 在 PHP 处理慢或者前端压力很大的情况下,很容易出现进程数飙升,从而拒绝服务的现象。
● nginx 处理静态文件好,静态处理性能比 apache 高三倍以上
● nginx 的设计高度模块化,编写模块相对简单
● nginx 配置简洁,正则配置让很多事情变得简单,而且改完配置能使用 -t 测试配置有没有问题,apache 配置复杂 ,重启的时候发现配置出错了,会很崩溃
● nginx 作为负载均衡服务器,支持 7 层负载均衡
● nginx 本身就是一个反向代理服务器,而且可以作为非常优秀的邮件代理服务器
● 启动特别容易, 并且几乎可以做到 7*24 不间断运行,即使运行数个月也不需要重新启动,还能够不间断服务的情况下进行软件版本的升级
● 社区活跃,各种高性能模块出品迅速
聊聊:Nginx如何处理HTTP请求。
Nginx使用反应器模式。
主事件循环等待操作系统发出准备事件的信号,这样数据就可以从套接字读取,在该实例中读取到缓冲区并进行处理。
单个线程可以提供数万个并发连接。
聊聊:Nginx是如何处理一个请求的呢?
首先,nginx在启动时,会解析配置文件,得到需要监听的端口与ip地址,
然后在nginx的master进程里面,先初始化好这个监控的socket,再进行listen
然后再fork出多个子进程出来, 子进程会竞争accept新的连接。
此时,客户端就可以向nginx发起连接了。
当客户端与nginx进行三次握手,与nginx建立好一个连接后,
此时,某一个子进程会accept成功,然后创建nginx对连接的封装,即ngx_connection_t结构体,
接着,根据事件调用相应的事件处理模块,如http模块与客户端进行数据的交换,
最后,nginx或客户端来主动关掉连接,到此,一个连接就寿终正寝了
聊聊:Nginx处理HTTP请求过程的 11 个阶段?
Nginx 处理 HTTP 请求的过程大概可以分为 11 个阶段,如下:
- Read Request Headers:解析请求头。
- Identify Configuration Block:识别由哪一个 location 进行处理,匹配 URL。
- Apply Rate Limits:判断是否限速。例如可能这个请求并发的连接数太多超过了限制,或者 QPS 太高。
- Perform Authentication:连接控制,验证请求。例如可能根据 Referrer 头部做一些防盗链的设置,或者验证用户的权限。
- Generate Content:生成返回给用户的响应。为了生成这个响应,做反向代理的时候可能会和上游服务(Upstream Services)进行通信,然后这个过程中还可能会有些子请求或者重定向,那么还会走一下这个过程(Internal redirects and subrequests)。
- Response Filters:过滤返回给用户的响应。比如压缩响应,或者对图片进行处理。
- Log:记录日志。
以上这七个步骤从整体上介绍了一下处理流程,下面还会再说一下实际的处理过程。
聊聊:Nginx处理HTTP请求的11个阶段
下面介绍一下详细的 11 个阶段,每个阶段都可能对应着一个甚至多个 HTTP 模块,通过这样一个模块对比,我们也能够很好的理解这些模块具体是怎么样发挥作用的。
- POST_READ:在 read 完请求的头部之后,在没有对头部做任何处理之前,想要获取到一些原始的值,就应该在这个阶段进行处理。这里面会涉及到一个 realip 模块。
- SERVER_REWRITE:和下面的 REWRITE 阶段一样,都只有一个模块叫 rewrite 模块,一般没有第三方模块会处理这个阶段。
- FIND_CONFIG:做 location 的匹配,暂时没有模块会用到。
- REWRITE:对 URL 做一些处理。
- POST_WRITE:处于 REWRITE 之后,也是暂时没有模块会在这个阶段出现。
接下来是确认用户访问权限的三个模块:
- PREACCESS:是在 ACCESS 之前要做一些工作,例如并发连接和 QPS 需要进行限制,涉及到两个模块:limt_conn 和 limit_req
- ACCESS:核心要解决的是用户能不能访问的问题,例如 auth_basic 是用户名和密码,access 是用户访问 IP,auth_request 根据第三方服务返回是否可以去访问。
- POST_ACCESS:是在 ACCESS 之后会做一些事情,同样暂时没有模块会用到。
最后的三个阶段处理响应和日志:
- PRECONTENT:在处理 CONTENT 之前会做一些事情,例如会把子请求发送给第三方的服务去处理,try_files 模块也是在这个阶段中。
- CONTENT:这个阶段涉及到的模块就非常多了,例如 index, autoindex, concat 等都是在这个阶段生效的。
- LOG:记录日志 access_log 模块。
以上的这些阶段都是严格按照顺序进行处理的,当然,每个阶段中各个 HTTP 模块的处理顺序也很重要,如果某个模块不把请求向下传递,后面的模块是接收不到请求的。
而且每个阶段中的模块也不一定所有都要执行一遍,下面就接着讲一下各个阶段模块之间的请求顺序。
聊聊:Nginx处理HTTP请求的11个阶段的顺序处理
如下图所示,每一个模块处理之间是有序的,那么这个顺序怎么才能得到呢?其实非常简单,在源码 ngx_module.c 中,有一个数组 ngx_module_name,其中包含了在编译 Nginx 的时候的 with 指令所包含的所有模块,它们之间的顺序非常关键,在数组中顺序是相反的。
char *ngx_module_names[] =
… …
"ngx_http_static_module",
"ngx_http_autoindex_module",
"ngx_http_index_module",
"ngx_http_random_index_module",
"ngx_http_mirror_module",
"ngx_http_try_files_module",
"ngx_http_auth_request_module",
"ngx_http_auth_basic_module",
"ngx_http_access_module",
"ngx_http_limit_conn_module",
"ngx_http_limit_req_module",
"ngx_http_realip_module",
"ngx_http_referer_module",
"ngx_http_rewrite_module",
"ngx_http_concat_module",
… …
灰色部分的模块是 Nginx 的框架部分去执行处理的,第三方模块没有机会在这里得到处理。
在依次向下执行的过程中,也可能不按照这样的顺序。例如,在 access 阶段中,有一个指令叫 satisfy,它可以指示当有一个满足的时候就直接跳到下一个阶段进行处理,例如当 access 满足了,就直接跳到 try_files 模块进行处理,而不会再执行 auth_basic、auth_request 模块。
在 content 阶段中,当 index 模块执行了,就不会再执行 auto_index 模块,而是直接跳到 log 模块。整个 11 个阶段所涉及到的模块和先后顺序如下图所示:
聊聊:Nginx配置文件nginx.conf有哪些属性模块?
worker_processes 1; # worker进程的数量
events # 事件区块开始
worker_connections 1024; # 每个worker进程支持的最大连接数
# 事件区块结束
http # HTTP区块开始
include mime.types; # Nginx支持的媒体类型库文件
default_type application/octet-stream; # 默认的媒体类型
sendfile on; # 开启高效传输模式
keepalive_timeout 65; # 连接超时
server # 第一个Server区块开始,表示一个独立的虚拟主机站点
listen 80; # 提供服务的端口,默认80
server_name localhost; # 提供服务的域名主机名
location / # 第一个location区块开始
root html; # 站点的根目录,相当于Nginx的安装目录
index index.html index.htm; # 默认的首页文件,多个用空格分开
# 第一个location区块结果
error_page 500502503504 /50x.html; # 出现对应的http状态码时,使用50x.html回应客户
location = /50x.html # location区块开始,访问50x.html
root html; # 指定对应的站点目录为html
......
聊聊:在Nginx中,如何使用未定义的服务器名称来阻止处理请求?
只需将请求删除的服务器, 可以定义为:
Server
listen 80;
server_name “ “ ;
return 444;
这里,服务器名被保留为一个空字符串,它将在没有“主机”头字段的情况下匹配请求,而一个特殊的Nginx的非标准代码444被返回,从而终止连接。
聊聊:Nginx的进程模型
- master-worker模式
在master-worker模式下,有一个master进程和至少一个的worker进程。 - 单进程模式。
单进程模式只有一个进程。
聊聊:Nginx服务器上的Master和Worker进程分别是什么?
Master进程:读取及评估配置和维持
Worker进程:处理请求
聊聊:Nginx惊群
惊群效应(thundering herd)是指多进程(多线程)在同时阻塞等待同一个事件的时候(休眠状态),
如果等待的这个事件发生,那么他就会唤醒等待的所有进程(或者线程),但是最终却只能有一个进程(线程)获得这个时间的 “控制权”,对该事件进行处理,
而其他进程(线程)获取 “控制权” 失败,只能重新进入休眠状态,这种现象和性能浪费就叫做惊群效应。
聊聊:惊群效应消耗了什么
Linux 内核对用户进程(线程)频繁地做无效的调度、上下文切换等使系统性能大打折扣。上下文切换(context switch)过高会导致 CPU 像个搬运工,频繁地在寄存器和运行队列之间奔波,更多的时间花在了进程(线程)切换,而不是在真正工作的进程(线程)上面。
直接的消耗包括 CPU 寄存器要保存和加载(例如程序计数器)、系统调度器的代码需要执行。间接的消耗在于多核 cache 之间的共享数据。为了确保只有一个进程(线程)得到资源,需要对资源操作进行加锁保护,加大了系统的开销。目前一些常见的服务器软件有的是通过锁机制解决的,比如 Nginx(它的锁机制是默认开启的,可以关闭);还有些认为惊群对系统性能影响不大,没有去处理,比如 Lighttpd。
聊聊:Nginx惊群效应? 以及解决方案
对于 Nginx 的惊群问题,我们首先需要理解的是,在 Nginx 启动过程中,master 进程会监听配置文件中指定的各个端口,然后 master 进程就会调用 fork() 方法创建各个子进程,根据进程的工作原理,子进程是会继承父进程的全部内存数据以及监听的端口的,也就是说 worker 进程在启动之后也是会监听各个端口的。
关于惊群,指的就是当客户端有新建连接的请求到来时,就会触发各个 worker 进程的连接建立事件,但是只有一个 worker 进程能够正常处理该事件,而其他的 worker 进程会发现事件已经失效,从而重新循环进入等待状态。这种由于一个事件而 “惊” 起了所有 worker 进程的现象就是惊群问题。很明显,如果所有的 worker 进程都被触发了,那么这将消耗大量的资源。
解决方案
在 Nginx 中,每个 worker 进程被创建的时候,都会调用 ngx_worker_process_init() 方法初始化当前 worker 进程,这个过程中有一个非常重要的步骤,即每个 worker 进程都会调用 epoll_create() 方法为自己创建一个独有的 epoll 句柄。
对于每一个需要监听的端口,都有一个文件描述符与之对应,而 worker 进程只有将该文件描述符通过 epoll_ctl() 方法添加到当前进程的 epoll 句柄中,并且监听 accept 事件,此时才会被客户端的连接建立事件触发,从而处理该事件。从这里也可以看出,worker 进程如果没有将所需要监听的端口对应的文件描述符添加到该进程的 epoll 句柄中,那么其是无法被触发对应的事件的。
基于这个原理,nginx 就使用了一个共享锁来控制当前进程是否有权限将需要监听的端口添加到当前进程的 epoll 句柄中,也就是说,只有获取锁的进程才会监听目标端口。通过这种方式,就保证了每次事件发生时,只有一个 worker 进程会被触发。如下图所示为 worker 进程工作循环的一个示意图:
这里关于图中的流程,需要说明的一点是,每个 worker 进程在进入循环之后就会尝试获取共享锁,如果没有获取到,就会将所监听的端口的文件描述符从当前进程的 epoll 句柄中移除(即使并不存在也会移除),这么做的主要目的是防止丢失客户端连接事件,即使这可能造成少量的惊群问题,但是并不严重。
试想一下,如果按照理论,在当前进程释放锁的时候就将监听的端口的文件描述符从 epoll 句柄中移除,那么在下一个 worker 进程获取锁之前,这段时间各个端口对应的文件描述符是没有任何 epoll 句柄进行监听的,此时就会造成事件的丢失。如果反过来,按照图中的在获取锁失败的时候才移除监听的文件描述符,由于获取锁失败,则说明当前一定有一个进程已经监听了这些文件描述符,因而此时移除是安全的。
但是这样会造成的一个问题是,按照上图,当前进程在一个循环执行完毕的时候,会释放锁,然后处理其他的事件,注意这个过程中其是没有释放所监听的文件描述符的。此时,如果另一个进程获取到了锁,并且监听了文件描述符,那么这个时候就有两个进程监听了文件描述符,因而此时如果客户端发生连接建立事件,那么就会触发两个
以上是关于Nginx面试题(史上最全 + 持续更新)的主要内容,如果未能解决你的问题,请参考以下文章