原文 40个 Nginx 常问面试题

Nginx 是一款轻量级的 Web 服务器、反向代理服务器,由于它的内存占用少,启动极快,高并发能力强,在互联网项目中广泛应用。

什么是 Nginx?

Nginx 是一个 轻量级 / 高性能的反向代理 Web 服务器,用于 HTTP、HTTPS、SMTP、POP3 和 IMAP 协议。他实现非常高效的反向代理、负载平衡,他可以处理 2-3 万并发连接数,官方监测能支持 5 万并发,现在中国使用 nginx 网站用户有很多,例如:新浪、网易、 腾讯等。

Nginx 有哪些优点?

跨平台、配置简单。

非阻塞、高并发连接:处理 2-3 万并发连接数,官方监测能支持 5 万并发。

内存消耗

小:开启 10 个 Nginx 才占 150M 内存。

成本低廉,且开源。

稳定性高,宕机的概率非常小。

内置的健康检查功能:如果有一个服务器宕机,会做一个健康检查,再发送的请求就不会发送到宕机的服务器了。重新将请求提交到其他的节点上

Nginx 应用场景?

http 服务器。Nginx 是一个 http 服务可以独立提供 http 服务。可以做网页静态服务器。

虚拟主机。可以实现在一台服务器虚拟出多个网站,例如个人网站使用的虚拟机。

反向代理,负载均衡。当网站的访问量达到一定程度后,单台服务器不能满足用户的请求时,需要用多台服务器集群可以使用 nginx 做反向代理。并且多台服务器可以平均分担负载,不会应为某台服务器负载高宕机而某台服务器闲置的情况。

nginz 中也可以配置安全管理、比如可以使用 Nginx 搭建 API 接口网关, 对每个接口服务进行拦截。

Nginx 怎么处理请求的?

1

2

3

4

5

6

7

server { # 第一个Server区块开始,表示一个独立的虚拟主机站点

listen 80;# 提供服务的端口,默认80

server_name localhost; # 提供服务的域名主机名

location / { # 第一个location区块开始

root html; # 站点的根目录,相当于Nginx的安装目录

index index.html index.html; # 默认的首页文件,多个用空格分开

} # 第一个location区块结果

首先,Nginx 在启动时,会解析配置文件,得到需要监听的端口与 IP 地址,然后在 Nginx 的 Master 进程里面先初始化好这个监控的 Socket(创建 S ocket,设置 addr、reuse 等选项,绑定到指定的 ip 地址端口,再 listen 监听)。

然后,再 fork(一个现有进程可以调用 fork 函数创建一个新进程。由 fork 创建的新进程被称为子进程) 出多个子进程出来。

之后,子进程会竞争 accept 新的连接。此时,客户端就可以向 nginx 发起连接了。当客户端与 nginx 进行三次握手,与 nginx 建立好一个连接后。此时,某一个子进程会 accept 成功,得到这个建立好的连接的 Socket ,然后创建 nginx 对连接的封装,即 ngx_connection_t 结构体。

接着,设置读写事件处理函数,并添加读写事件来与客户端进行数据的交换。

最后,Nginx 或客户端来主动关掉连接,到此,一个连接就寿终正寝了。

Nginx 是如何实现高并发的?

如果一个 server 采用一个进程 (或者线程) 负责一个 request 的方式,那么进程数就是并发数。那么显而易见的,就是会有很多进程在等待中。等什么?最多的应该是等待网络传输。

而 Nginx 的异步非阻塞工作方式正是利用了这点等待的时间。在需要等待的时候,这些进程就空闲出来待命了。因此表现为少数几个进程就解决了大量的并发问题。

Nginx 是如何利用的呢,简单来说:同样的 4 个进程,如果采用一个进程负责一个 request 的方式,那么,同时进来 4 个 request 之后,每个进程就负责其中一个,直至会话关闭。期间,如果有第 5 个 request 进来了。就无法及时反应了,因为 4 个进程都没干完活呢,因此,一般有个调度进程,每当新进来了一个 request ,就新开个进程来处理。

回想下,BIO 是不是存在酱紫的问题?

Nginx 不这样,每进来一个 request ,会有一个 worker 进程去处理。但不是全程的处理,处理到什么程度呢?处理到可能发生阻塞的地方,比如向上游(后端)服务器转发 request ,并等待请求返回。那么,这个处理的 worker 不会这么傻等着,他会在发送完请求后,注册一个事件:“如果 upstream 返回了,告诉我一声,我再接着干”。于是他就休息去了。此时,如果再有 request 进来,他就可以很快再按这种方式处理。而一旦上游服务器返回了,就会触发这个事件,worker 才会来接手,这个 request 才会接着往下走。

这就是为什么说,Nginx 基于事件模型。

由于 web server 的工作性质决定了每个 request 的大部份生命都是在网络传输中,实际上花费在 server 机器上的时间片不多。这是几个进程就解决高并发的秘密所在。即:

webserver 刚好属于网络 IO 密集型应用,不算是计算密集型。

异步,非阻塞,使用 epoll ,和大量细节处的优化。也正是 Nginx 之所以然的技术基石。

什么是正向代理?

一个位于客户端和原始服务器 (origin server) 之间的服务器,为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),然后代理向原始服务器转交请求并将获得的内容返回给客户端。

客户端才能使用正向代理。正向代理总结就一句话:代理端代理的是客户端。例如说:我们使用的 OpenVPN 等等。

什么是反向代理?

反向代理(Reverse Proxy)方式,是指以代理服务器来接受 Internet 上的连接请求,然后将请求,发给内部网络上的服务器并将从服务器上得到的结果返回给 Internet 上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。

反向代理总结就一句话:代理端代理的是服务端。

反向代理服务器的优点是什么?

反向代理服务器可以隐藏源服务器的存在和特征。它充当互联网云和 web 服务器之间的中间层。这对于安全方面来说是很好的,特别是当您使用 web 托管服务时。

Nginx 目录结构有哪些?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

tree /usr/local/nginx

/usr/local/nginx

├── client_body_temp

├── conf # Nginx所有配置文件的目录

│ ├── fastcgi.conf # fastcgi相关参数的配置文件

│ ├── fastcgi.conf.default # fastcgi.conf的原始备份文件

│ ├── fastcgi_params # fastcgi的参数文件

│ ├── fastcgi_params.default

│ ├── koi-utf

│ ├── koi-win

│ ├── mime.types # 媒体类型

│ ├── mime.types.default

│ ├── nginx.conf # Nginx主配置文件

│ ├── nginx.conf.default

│ ├── scgi_params # scgi相关参数文件

│ ├── scgi_params.default

│ ├── uwsgi_params # uwsgi相关参数文件

│ ├── uwsgi_params.default

│ └── win-utf

├── fastcgi_temp # fastcgi临时数据目录

├── html # Nginx默认站点目录

│ ├── 50x.html # 错误页面优雅替代显示文件,例如当出现502错误时会调用此页面

│ └── index.html # 默认的首页文件

├── logs # Nginx日志目录

│ ├── access.log # 访问日志文件

│ ├── error.log # 错误日志文件

│ └── nginx.pid # pid文件,Nginx进程启动后,会把所有进程的ID号写到此文件

├── proxy_temp # 临时目录

├── sbin # Nginx命令目录

│ └── nginx # Nginx的启动命令

├── scgi_temp # 临时目录

└── uwsgi_temp # 临时目录

Nginx 配置文件 nginx.conf 有哪些属性模块?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

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

}

}

......

cookie 和 session 区别?

共同:

存放用户信息。存放的形式:key-value 格式 变量和变量内容键值对。

区别:

cookie

存放在客户端浏览器

每个域名对应一个 cookie,不能跨跃域名访问其他 cookie

用户可以查看或修改 cookie

http 响应报文里面给你浏览器设置

钥匙(用于打开浏览器上锁头)

session:

存放在服务器(文件,数据库,redis)

存放敏感信息

锁头

为什么 Nginx 不使用多线程?

Apache: 创建多个进程或线程,而每个进程或线程都会为其分配 cpu 和内存(线程要比进程小的多,所以 worker 支持比 perfork 高的并发),并发过大会榨干服务器资源。

Nginx: 采用单线程来异步非阻塞处理请求(管理员可以配置 Nginx 主进程的工作进程的数量)(epoll),不会为每个请求分配 cpu 和内存资源,节省了大量资源,同时也减少了大量的 CPU 的上下文切换。所以才使得 Nginx 支持更高的并发。

Nginx 怎么做的动静分离?

只需要指定路径对应的目录。location / 可以使用正则表达式匹配。并指定对应的硬盘中的目录。如下:(操作都是在 Linux 上)

1

2

3

4

location /image/ {

root /usr/local/static/;

autoindex on;

}

步骤:

1

2

3

4

5

6

7

8

9

10

11

# 创建目录

mkdir /usr/local/static/image

# 进入目录

cd /usr/local/static/image

# 上传照片

photo.jpg

# 重启nginx

sudo nginx -s reload

打开浏览器 输入 server_name/image/1.jpg 就可以访问该静态图片了

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

为了避免服务器崩溃,大家会通过负载均衡的方式来分担服务器压力。将对台服务器组成一个集群,当用户访问时,先访问到一个转发服务器,再由转发服务器将访问分发到压力更小的服务器。

Nginx 负载均衡实现的策略有以下五种:

轮询 (默认)

每个请求按时间顺序逐一分配到不同的后端服务器,如果后端某个服务器宕机,能自动剔除故障系统。

1

2

3

4

upstream backserver {

server 192.168.0.12;

server 192.168.0.13;

}

权重 weight

weight 的值越大,分配到的访问概率越高,主要用于后端每台服务器性能不均衡的情况下。其次是为在主从的情况下设置不同的权值,达到合理有效的地利用主机资源。

1

2

3

4

5

# 权重越高,在被访问的概率越大,如上例,分别是20%,80%。

upstream backserver {

server 192.168.0.12 weight=2;

server 192.168.0.13 weight=8;

}

ip_hash(IP 绑定)

每个请求按访问 IP 的哈希结果分配,使来自同一个 IP 的访客固定访问一台后端服务器,并且可以有效解决动态网页存在的 session 共享问题

1

2

3

4

5

upstream backserver {

ip_hash;

server 192.168.0.12:88;

server 192.168.0.13:80;

}

fair(第三方插件)

必须安装 upstream_fair 模块。

对比 weight、ip_hash 更加智能的负载均衡算法,fair 算法可以根据页面大小和加载时间长短智能地进行负载均衡,响应时间短的优先分配。

1

2

3

4

5

6

# 哪个服务器的响应速度快,就将请求分配到那个服务器上。

upstream backserver {

server server1;

server server2;

fair;

}

url_hash(第三方插件)

必须安装 Nginx 的 hash 软件包

按访问 url 的 hash 结果来分配请求,使每个 url 定向到同一个后端服务器,可以进一步提高后端缓存服务器的效率。

1

2

3

4

5

6

upstream backserver {

server squid1:3128;

server squid2:3128;

hash $request_uri;

hash_method crc32;

}

location 的作用是什么?

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

location 的语法能说出来吗?

注意:~ 代表自己输入的英文字母

匹配符

匹配规则

优先级

=

精确匹配

1

^~

以某个字符串开头

2

~

区分大小写的正则匹配

3

~*

不区分大小写的正则匹配

4

!~

区分大小写不匹配的正则

5

!~*

不区分大小写的不匹配正则

6

/

通用匹配,任何请求都会匹配到

7

Location 正则案例

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

# 优先级1,精确匹配,根路径

location =/ {

return 400;

}

# 优先级2,以某个字符串开头,以av开头的,优先匹配这里,区分大小写

location ^~ /av {

root /data/av/;

}

# 优先级3,区分大小写的正则匹配,匹配/media*****路径

location ~ /media {

alias /data/static/;

}

# 优先级4 ,不区分大小写的正则匹配,所有的****.jpg|gif|png 都走这里

location ~* .*\.(jpg|gif|png|js|css)$ {

root /data/av/;

}

# 优先7,通用匹配

location / {

return 403;

}

Nginx 配置高可用性怎么配置?

当上游服务器 (真实访问服务器),一旦出现故障或者是没有及时相应的话,应该直接轮训到下一台服务器,保证服务器的高可用

Nginx 配置代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

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 怎么判断别 IP 不可访问?

1

2

3

4

# 如果访问的ip地址为192.168.9.115,则返回403

if ($remote_addr = 192.168.9.115) {

return 403;

}

怎么限制浏览器访问?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

## 不允许谷歌浏览器访问 如果是谷歌浏览器返回500

if ($http_user_agent ~ Chrome) {

return 500;

}

Rewrite全局变量是什么?

$remote_addr //获取客户端ip

$binary_remote_addr //客户端ip(二进制)

$remote_port //客户端port,如:50472

$remote_user //已经经过Auth Basic Module验证的用户名

$host //请求主机头字段,否则为服务器名称,如:blog.sakmon.com

$request //用户请求信息,如:GET ?a=1&b=2 HTTP/1.1

$request_filename //当前请求的文件的路径名,由root或alias和URI request组合而成,如:/2013/81.html

$status //请求的响应状态码,如:200

$body_bytes_sent // 响应时送出的body字节数数量。即使连接中断,这个数据也是精确的,如:40

$content_length // 等于请求行的“Content_Length”的值

$content_type // 等于请求行的“Content_Type”的值

$http_referer // 引用地址

$http_user_agent // 客户端agent信息,如:Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/29.0.1547.76 Safari/537.36

$args //与$query_string相同 等于当中URL的参数(GET),如a=1&b=2

$document_uri //与$uri相同 这个变量指当前的请求URI,不包括任何参数(见$args) 如:/2013/81.html

$document_root //针对当前请求的根路径设置值

$hostname //如:centos53.localdomain

$http_cookie //客户端cookie信息

$cookie_COOKIE //cookie COOKIE变量的值

$is_args //如果有$args参数,这个变量等于”?”,否则等于”",空值,如?

$limit_rate //这个变量可以限制连接速率,0表示不限速

$query_string // 与$args相同 等于当中URL的参数(GET),如a=1&b=2

$request_body // 记录POST过来的数据信息

$request_body_file //客户端请求主体信息的临时文件名

$request_method //客户端请求的动作,通常为GET或POST,如:GET

$request_uri //包含请求参数的原始URI,不包含主机名,如:/2013/81.html?a=1&b=2

$scheme //HTTP方法(如http,https),如:http

$uri //这个变量指当前的请求URI,不包括任何参数(见$args) 如:/2013/81.html

$request_completion //如果请求结束,设置为OK. 当请求未结束或如果该请求不是请求链串的最后一个时,为空(Empty),如:OK

$server_protocol //请求使用的协议,通常是HTTP/1.0或HTTP/1.1,如:HTTP/1.1

$server_addr //服务器IP地址,在完成一次系统调用后可以确定这个值

$server_name //服务器名称,如:blog.sakmon.com

$server_port //请求到达服务器的端口号,如:80

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

在有多个 cpu 的情况下,可以设置多个 worker,worker 进程的数量可以设置到和 cpu 的核心数一样多,如果在单个 cpu 上起多个 worker 进程,那么操作系统会在多个 worker 之间进行调度,这种情况会降低系统性能,如果只有一个 cpu,那么只启动一个 worker 进程就可以了。

参考

40个 Nginx 常问面试题

Nginx的那些实战骚操作

图解Nginx负载均衡的几种策略