Nginx面试常问题&工作原理揭秘

原文参考:

blog.csdn.net/wuzhiwei549/article/details/1227589

吐血给大家整理了一些面试官常问到的有关Nginx的问题

Nginx面试常问题

在这里插入图片描述

什么是Nginx?

Nginx是一个轻量级(消耗资源小)、高性能(处理2-3万并发连接数)的反向代理web服务器,支持HTTP、HTTPS、SMTP、POP3 和 IMAP 协议;可以实现反向代理、负载均衡的功能

Nginx有哪些优点

  1. 跨平台、配置简单
  2. 采用异步非阻塞网络I/O,支持高并发连接(官方监测能支持 5 万并发)
  3. 轻量,开启10个Nginx才占150M内存
  4. 开源,成本低
  5. 稳定性高,支持不停机升级(热升级)
  6. 内置健康检查功能,实现服务的高可用

Nginx有哪些常见应用场景?

  1. Http服务器。Nginx支持Http、Https协议。可以提供Http服务,称为网页静态服务器
  2. 虚拟主机。Nginx可以在一台服务器上搭建多个虚拟网站
  3. 负载均衡。当面对一些高并发大流量的场景时,Nginx可以将流量均衡的分发到后端的服务器集群,使后端每台服务器能够平坦压力;而且对客户端是透明的,即客户端认为自己访问的是后端服务器,其实访问的是Nginx服务器,Nginx服务器对后端服务器集群进行了隔离保护
  4. 反向代理。Nginx服务器将客户端请求代理到后端服务器集群上。对客户端透明,即客户端认为自己访问的是后端服务器,其实访问的是Nginx服务器;对后端服务器进行了隔离,保证了后端服务器的安全

Nginx怎么实现高并发

一句话概括:采用异步非阻塞IO机制+epoll事件驱动模型

简单来说,每当有一个请求进来,就会有一个worker进程去负责处理,但不是全程负责,它处理到可能发生阻塞的地方(比如说向后端服务器转发request,并等待后端服务器返回结果),在等待返回结果这段时间里worker进程不会傻傻等着,它会在转发完请求后注册一个事件——“如果结果返回了就通知我一声,我再接着处理”,之后就休息去了

如果再有请求进来,worker进程就可以按上面那种方式去处理请求;一旦后端服务器的结果返回了,worker才会回来继续处理请求

关于这个内容我会专门拿出一个章节来讲

正向代理&反向代理

  • 正向代理

位于客户端和后端服务器之间,将客户端的请求代理到后端服务器(替客户端去发起请求)

客户端明确的知道要访问的服务器地址。但是后端服务器只清楚是哪个代理服务器发来请求,它并不知道这个请求来自于哪个具体的客户端(即屏蔽了客户端的具体信息)

例子:VPN、运营商

  • 反向代理
    在这里插入图片描述

位于客户端和后端服务器之间,多个客户端给服务器发送请求,反向代理服务器(例如Nginx)收到之后,按照一定的规则将请求分发给后面的服务器来处理

反向代理模式将后端服务器隔离起来(客户端并不知道自己访问的是一个代理),能够保证后端服务器的安全

为什么Nginx不使用多线程

Nginx是多进程/单线程模式,进程之间是相互独立的,不共享资源,不需要加锁,一 个worker进程挂了不会影响到其他worker进程

一个进程退出后, 其它进程还在工作,服务不会中断,master 进程则很快重新启动新的 worker 进程

Nginx和Apache的异同之处

  • 相同点

    • web服务器,提供http服务
    • 可以实现负载均衡和反向代理的功能
  • 差异点

    • Nginx是一个基于事件的web服务器;apache是一个基于流程的服务器
    • Nginx采取异步非阻塞网络IO;Apache采取了同步阻塞网络IO;Nginx的抗高并发能力会更好,可以同时处理更多请求
    • Nginx更加轻量化,占用更少的内存和资源
    • Apache性能更稳定,比Nginx的bug要少

Nginx负载均衡的算法怎么实现的?策略有哪些?

  • RR(轮询)

将接收到的请求按照顺序逐一分发到不同的后端服务器,如果某台后端服务器出现故障或者宕机,会自动去除故障服务器,使用户访问不受影响

upstream backserver { 
 server 192.168.0.1; 
 server 192.168.0.2; 
} 
  • weight(加权轮询)

指带权重值的轮询算法,weight值越大,分配到的概率也就越高

主要用于后端每台服务器性能不均衡的情况,或者说仅仅为在主从的情况下设置不同的权重(weight)值,可以有效的利用服务器资源

# 权重越高,在被访问的概率越大。如例,分别是20%,80%。
upstream backserver { 
 server 192.168.0.1 weight=2; 
 server 192.168.0.2 weight=8; 
} 
  • ip_hash(ip绑定)

将来自于同一个源ip地址的请求始终分发到第一次分配到的后端服务器

upstream backserver { 
 ip_hash; 
 server 192.168.0.1; 
 server 192.168.0.2; 
} 
  • url_hash(url哈希)

按照访问的url的哈希结果来进行分配,每个相同请求的url会被定向到某一台后端服务器,进一步提高后端服务器缓存的效率

如果要使用这种算法,则需要安装Nginx的hash软件包

upstream backserver { 
 server squid1:3128; 
 server squid2:3128; 
 hash $request_uri; 
 hash_method crc32; 
} 
  • fair(智能调度)

以根据后端服务器的响应请求时间智能地进行均衡分发,比如说响应时间短的优先分配。如果要使用这种算法,则需要安装upstream_fair模块

# 哪个服务器的响应速度快,就将请求分配到那个服务器上。
upstream backserver { 
 server server1; 
 server server2; 
 fair; 
} 

能聊聊location吗

location的作用是根据用户请求的URI来执行不同的应用,也就是根据用户请求的网站URL进行匹配,匹配成功即进行相关的操作

  • location优先级
匹配符匹配规则优先级
=精确匹配1
^~以某个字符串开头普通匹配2
~区分大小写的正则匹配3
~*不区分大小写的正则匹配4
!~区分大小写不匹配的正则5
!~*不区分大小写不匹配的正则6
/普通匹配,任何请求都会匹配到7
  • location匹配规则

首先精确匹配优先级最高,如果没有精确匹配的话就进行普通匹配,然后保存具有最长匹配前缀的结果(注意这里是保存,而不是匹配完成)

保存结果后开始正则匹配,如果有正则匹配则按照配置里正则表达式出现的顺序进行匹配(匹配最先出现那个),匹配到之后返回正则匹配结果

如果没有正则匹配就返回具有最长匹配前缀的结果(即一开始保存那个)

PS:如果最长匹配前缀有**^~**,则忽略正则匹配,直接返回结果

Nginx如何实现后端服务健康检查

  • 利用 nginx 自带模块 ngx_http_proxy_module 和 ngx_http_upstream_module 对后端节点做健康检查
  • 利用 nginx_upstream_check_module 模块对后端节点做健康检查(推荐使用这个

生产中如何设置worker进程的数量呢

推荐设置成auto模式,即根据系统CPU个数来自动分配worker进程

Nginx 如何处理高并发

在Nginx里面,一般我们使用异步非阻塞处理请求方式+epoll机制的IO多路复用网络I/O模型

什么是异步同步,什么是阻塞非阻塞

  • 异步:程序执行 I/O 操作后,不用等待完成和完成后的响应,而是继续执行就可以。等到这次 I/O 完成后,响应会用事件通知的方式,告诉应用程序
  • 同步:程序执行 I/O 操作后,要一直等到整个 I/O 完成后,才能获得 I/O 响应
  • 阻塞:程序执行I/O操作后,如果没有获得响应,就会阻塞当前线程,自然就不能执行其他任务
  • 非阻塞:程序执行I/O操作后,不会阻塞当前线程,可以继续执行其他任务,随后通过轮询或者事件通知的形式获取调用的结果

在Nginx里面,每个worker进程接收到客户端的请求之后,会进行相关的操作然后等待响应/结果,在等待的这个过程中worker进程是可以去处理其他请求的(非阻塞)

而Nginx客户端这时候也无需等待,可以去处理其他事件(异步)

如果响应/结果返回,就会通知worker进程(事件通知),这时候worker就会去返回处理请求

I/O事件通知

在介绍I/O多路复用之前,我们先来看一下I/O事件通知

I/O事件通知可分为水平触发(Level Trigger)和边缘触发(Eage Trigger)

  • 水平触发(LT)

文件描述符的状态没有改变(可以非阻塞的执行I/O),就会触发通知。也就是说,应用程序可以随时检查文件描述符的状态,然后再根据状态,进行 I/O 操作

读操作:

  1. 读缓冲区中有数据,且数据被读出一部分缓冲区还不为空

写操作:

  1. 写缓冲区还没满,还能继续写
  • 边缘触发(ET)

只有在文件描述符的状态发生改变(也就是 I/O 请求达到)时,才发送一次通知

这时候,应用程序需要尽可能多地执行 I/O,直到无法继续读写,才可以停止。

如果 I/O 没执行完,或者因为某种原因没来得及处理,那么这次通知也就丢失了

读操作:

  1. buffer由不可读状态变为可读状态(由空变为不空)
  2. 当有新数据到达时,即缓冲区中的待读数据变多的时候
  3. 当缓冲区有数据可读,且应用进程对相应的描述符进行EPOLL_CTL_MOD 修改EPOLLIN事件时

写操作:

  1. buffer由不可写变为可写(由空变为不空)
  2. 缓冲区中的内容变少(有数据被发送走)
  3. 当缓冲区有空间可写,且应用进程对相应的描述符进行EPOLL_CTL_MOD 修改EPOLLOUT事件时

IO多路复用

I/O多路复用也就是我们见到的select、poll、epoll

  • select和poll

selct和poll均采用非阻塞I/O+水平触发通知

selct和poll从文件描述符中找出哪些可以进行I/O操作,然后执行,由于是非阻塞I/O,一个线程可以同时监控一批套接字的文件描述符,就达到了单线程处理多请求的目的

但是selct和poll是对套接字的文件描述符列表进行轮询,请求多的时候就会比较耗时

而且程序每次调用select和poll时,需要把fd列表从用户空间传入内核空间,内核修改后再传出用户空间,消耗了两次系统调用,增加了处理成本

select使用固定长度的数组来表示fd的集合,所以会有最大描述符数量的限制,但是poll使用pollfd指针,没有最大描述符数量的限制(优于select)

  • epoll

epoll使用红黑树,在内核中管理fd的集合,相较于select和poll少了两次系统调用,减少成本

epoll使用事件驱动的机制,而非select和poll的轮询;只关注有I/O事件发生的fd

由于epoll采用边缘触发通知机制,当有边缘触发时,应用程序就需要尽可能地多执行I/O

#为什么边缘触发机制下一旦有通知程序要尽可能I/O?
因为边缘触发的原理是文件描述符的状态发生改变才会触发通知,之后便不再触发
例如进行读操作时,buffer有不可读变为可读,触发一次边缘通知,程序开始读buffer里的内容,如果没有读完,buffer里面剩余的内容是不会再次触发边缘通知的,就有可能导致读数据丢失

Nginx常见工作模型

  • 主进程 + 多个 worker 子进程

这是最常用的一种模型,主进程fork出多个子进程,每个子进程来处理请求

为啥使用多进程模式的Nginx性能还这么好?

其实这些worker进程实际上并不需要经常创建和销毁,而是在没任务时休眠,有任务时唤醒,只有在 worker 由于某些异常退出时,主进程才需要创建新的进程来代替它
在这里插入图片描述
但是这种模型会存在一个惊群问题,即当有网络请求过来时,多个进程会被同时唤醒,但实际上只有一个进程来响应这个事件,其他被唤醒的进程都会重新休眠

为了避免惊群问题,Nginx在每个worker进程中都增加了一个全局锁((accept_mutex)。这些 worker 进程需要首先竞争到锁,只有竞争到锁的进程,才会加入到 epoll 中,这样就确保只有一个 worker 子进程被唤醒)

  • 多进程监听相同端口模型

在这种方式下,所有的进程都监听相同的接口,并且开启 SO_REUSEPORT 选项,由内核负责将请求负载均衡到这些监听进程中去

由于内核确保了只有一个进程被唤醒,就不会出现惊群问题了
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

咸鱼Linux运维

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值