面试官:说说看Nginx是如何处理请求的?为什么Nginx不采用多线程模型?Nginx负载均衡的算法有哪些?什么是正向和反向代理

面试官:说说看Nginx是如何处理请求的?

当客户端发起一个请求时,Nginx的工作进程会监听网络端口,接收客户端的连接请求。以下是Nginx处理请求的具体流程:

(1) 接收连接请求:Nginx接收到客户端的连接请求后,会为该连接分配一个连接对象(ngx_connection_t)。连接对象包含连接的状态信息、读写事件处理器等。

(2) 读取请求头信息:Nginx从客户端读取请求头信息。请求头包含HTTP方法(如GET、POST)、URL、HTTP版本以及各种请求头字段(如Host、User-Agent、Content-Length等)。

(3) 解析请求头信息:Nginx解析请求头信息,提取必要的参数,如请求方法、URI、Host等。解析后的请求头信息存储在ngx_http_request_t结构体中。

(4) 查找匹配的虚拟主机和location块:

Nginx根据请求头中的Host字段查找匹配的虚拟主机(server块)。每个虚拟主机可以配置不同的域名和监听端口。在找到匹配的虚拟主机后,Nginx继续查找与请求URI匹配的location块。location块定义了如何处理特定路径的请求。

(5) 执行处理阶段:Nginx的请求处理分为多个阶段,每个阶段可以由多个模块处理。这些阶段包括:

rewrite phase:执行重写规则,如URL重写。post rewrite phase:处理重写后的请求。preaccess phase:执行访问控制前的检查,如IP地址过滤。access phase:执行访问控制,如身份验证。postaccess phase:访问权限控制后的处理。try-files:尝试访问文件或目录。content phase:生成响应内容,如静态文件服务、反向代理、FastCGI等。在这个阶段,Nginx根据配置生成响应内容,这可能涉及读取静态文件、调用后端服务(如反向代理、FastCGI、uWSGI等)、生成动态内容等。

(6) 生成并发送响应:

Nginx将生成的响应头发送回客户端。响应头包含HTTP状态码、响应头字段(如Content-Type、Content-Length等)。Nginx将生成的响应体发送回客户端。响应体可以是静态文件内容、后端服务返回的数据等。

(7) 关闭连接:一旦响应发送完毕,Nginx会关闭连接。如果启用了keep-alive连接,则连接可以保持打开状态,用于后续请求。

面试官:说说看Nginx的进程架构是怎样的?为什么Nginx不使用多线程模型?

1. 进程模型

Nginx采用Master-Worker多进程架构,这种架构的设计可以确保责任分离,以便更好地管理系统资源、并发请求处理与故障恢复。

(1) Master-Worker架构:

① 主进程(Master Process):

Nginx的核心组件,负责初始化Nginx、加载配置文件、创建Worker进程等。监听配置文件的变更,并在不重启的情况下重新加载配置。管理Worker进程的生命周期,包括启动、停止和管理Worker进程。不直接处理客户端的请求,而是用于控制和管理Worker进程。

② 工作进程(Worker Process)

Nginx的工作进程,负责处理客户端的请求。每个Worker进程都是一个完整的Nginx服务器,多个Worker进程之间是对等的。每个Worker进程可以处理成千上万的并发连接,Nginx的事件模型可以根据系统负载自动选择合适的事件通知机制(如epoll)。

(2) 进程间协作:

Master进程和Worker进程之间通过信号和共享内存进行通信。Master进程会向Worker进程发送信号以管理它们的生命周期(如启动、停止、重启等)。Worker进程之间通过共享内存和进程间通信(IPC)机制进行必要的数据共享和同步。

(3) 负载均衡:

Nginx通过多进程模型实现了负载均衡。当有新的客户端连接请求到达时,这些连接会被平均分配给各个Worker进程,从而实现负载均衡。这种设计确保了Nginx能够高效地处理大量并发连接,避免了单个进程成为瓶颈。

(4) 高可用性:

Nginx的多进程模型还提供了高可用性。当一个Worker进程出现故障时,Master进程会自动重新启动一个新的Worker进程来替代原来的进程,从而保证服务器的高可用性。这种设计使得Nginx能够在高负载和复杂环境下稳定运行。

2. 为什么Nginx不使用多线程模型?

Nginx选择不使用多线程模型,而是采用多进程加异步非阻塞I/O的事件驱动模型,主要基于以下几个原因:

(1) 资源隔离与稳定性

多进程模型下,每个工作进程都是独立的,它们之间不会共享内存空间(除了通过共享内存等特定机制进行通信)。这种隔离性使得一个工作进程的崩溃不会影响到其他进程,从而提高了整个系统的稳定性。在多线程模型中,线程之间共享进程的内存空间,这可能导致线程间的数据竞争、死锁等问题,增加了系统的复杂性和调试难度。

(2) 利用多核CPU

Nginx的多进程模型可以很好地利用现代操作系统提供的进程调度机制,将工作进程分配到不同的CPU核心上运行,从而实现并行处理。虽然多线程模型也可以利用多核CPU,但线程的创建、切换和同步开销通常比进程更高,尤其是在高并发场景下。

(3) 避免线程竞争和死锁

在多线程模型中,多个线程可能同时访问共享资源(如内存、文件等),这需要使用锁机制来确保数据的一致性和安全性。然而,锁的使用往往会导致线程竞争和死锁问题,降低系统的性能。Nginx通过采用异步非阻塞I/O和事件驱动模型,避免了锁的使用,从而减少了线程竞争和死锁的风险。

(4) 简化编程模型

Nginx的多进程加异步非阻塞I/O模型相对简单明了,开发者可以更容易地理解和维护代码。多线程编程往往涉及复杂的线程同步和通信机制,增加了编程的复杂性和出错的可能性。

(5) 设计初衷

Nginx的设计初衷就是为了提供一个高性能、低资源消耗的Web服务器和反向代理服务器。在设计之初,Nginx的开发者就选择了多进程加异步非阻塞I/O的模型,并一直沿用至今。Nginx的社区和开发者群体也倾向于保持这种设计哲学,以确保Nginx的稳定性和性能优势。

面试官:什么是正向代理和反向代理?Nginx如何实现正向代理和反向代理功能?

反向代理功能是指代理服务器接受互联网上的连接请求,然后将这些请求转发给内部网络上的服务器,并将从内部服务器上得到的响应返回给互联网上请求连接的客户端。在这个过程中,代理服务器在外部世界中显示为服务器。

一般来说反向代理中代理服务器和后台服务是一伙儿的,绑定在一起。客户端不知道自己实际请求的到底是谁。

现实中的反向代理例子有:负载均衡服务器、网络安全防护(防DDoS攻击) 和内容分发网络 CDN等。

Nginx实现反向代理功能主要通过配置Nginx服务器,使其成为客户端和目标服务器之间的中介。以下是Nginx实现反向代理功能的具体步骤和要点:

(1) 配置Nginx:

Nginx的反向代理配置主要在nginx.conf文件中进行,或者在包含的子配置文件中进行。

监听端口:设置Nginx监听的端口,默认为80端口,用于接收HTTP请求。服务器名称:定义Nginx服务器响应的域名。location块:根据请求的URI进行匹配,并定义相应的操作,如反向代理。反向代理指令(proxy_pass):指定请求应被转发到的后端服务器的URL。

(2) 配置示例:

一个基本的反向代理配置示例如下:

复制
http { server { listen 80; # 监听80端口 server_name example.com; # 服务器名称 location / { proxy_pass http://backend-server:8080; # 后端服务器地址与端口 proxy_set_header Host $host; # 保留原始Host头 proxy_set_header X-Real-IP $remote_addr; # 传递真实客户端IP proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 传递请求协议(http/https) # 其他可选配置,如缓存、超时、重试等 } } }1.2.3.4.5.6.7.8.9.10.11.12.13.14.

正向代理用于将客户端的请求转发到目标服务器,并将服务器的响应返回给客户端。在这种方式下,客户端将请求发送给代理服务器,由代理服务器代替客户端向目标服务器发出请求,并将目标服务器的响应返回给客户端。通过正向代理,客户端可以直接访问外部网络,而无需直接与目标服务器建立连接。

现实中的正向代理例子有:VPN。

Nginx作为高性能的Web服务器和反向代理服务器,也可以实现正向代理功能。以下是Nginx实现正向代理的具体步骤和配置方法:

Nginx正向代理的配置方法

由于默认的Nginx发布版本不支持正向代理功能,需要借助ngx_http_proxy_connect_module这个第三方插件来完成。在配置文件中添加正向代理的配置,例如:

复制
server { listen 3128; # 监听端口 resolver 114.114.114.114; # DNS解析器 proxy_connect; # 允许CONNECT请求 proxy_connect_allow 443 563; # 允许连接的端口 proxy_connect_connect_timeout 10s; # 连接超时时间 proxy_connect_data_timeout 10s; # 数据传输超时时间 location / { proxy_pass http://$host; # 转发请求到目标服务器 proxy_set_header Host $host; # 设置请求头 } }1.2.3.4.5.6.7.8.9.10.11.12.

以上配置中,listen指令指定了Nginx监听的端口,resolver指令指定了DNS解析器的地址,proxy_connect等指令用于配置CONNECT请求的处理,location指令和proxy_pass指令则用于指定将请求转发到哪个目标服务器。

面试官:什么是动态资源、静态资源分离?为什么要做动、静分离?Nginx怎么做的动静分离?

动态资源与静态资源分离(简称动、静分离)是一种常见的Web应用架构模式。

1. 动态资源与静态资源

(1) 静态资源

当用户多次访问某个资源时,如果资源的源代码不会发生改变,那么该资源就被称为静态资源。常见的静态资源包括图片(img)、样式表(css)、脚本文件(js)、视频(mp4)等。这些资源通常可以被浏览器和CDN(内容分发网络)缓存,以减少对服务器的重复请求。

(2) 动态资源当

用户多次访问某个资源时,如果资源的源代码可能会发生变化,那么该资源就被称为动态资源。常见的动态资源包括JSP、FTL等服务器端脚本或模板文件。这些资源通常需要根据用户的请求动态生成响应内容。

2. 动、静分离的原因

(1) 性能优化

动态内容和静态内容在处理和分发上存在差异。将它们分离可以分别进行优化,从而提高整体性能。例如,静态资源可以由专门的静态资源服务器(如Nginx)直接处理和提供,而动态资源则由应用服务器处理。这样可以显著减少对动态资源服务器的请求量,降低其负载。

(2) 缓存管理

静态资源易于被缓存,而动态资源通常不适宜缓存或需要更精细的缓存控制。通过动、静分离,可以更好地管理缓存策略,提高缓存命中率,减少服务器响应时间和带宽消耗。

(3) 负载均衡

分离后,可以根据内容类型对资源进行优化分配,实现更有效的负载均衡。例如,可以根据动态资源的访问量和特点,针对性地增加动态资源服务器的数量和规模,以应对高并发的访问需求。

(4) 安全性增强

静态内容服务器通常不需要执行复杂的程序代码,因此攻击面较小,可以降低安全风险。将其与执行动态代码的服务器分离可以降低潜在的安全威胁。

以下是Nginx动静分离的具体实现方式:

3. 配置方法

在Nginx中,可以通过location指令来实现动静分离。location指令用于匹配请求的URI,并根据不同的路径将请求分发给不同的处理模块。

(1) 静态资源处理

在上述配置中,location /static/和location /images/分别匹配以/static/和/images/开头的请求,Nginx将在指定的root目录下查找对应的文件。如果文件存在,Nginx会直接将文件返回给客户端。location ~* \.(jpg|jpeg|png|gif|ico|css|js)$使用正则表达式匹配所有以这些扩展名结尾的请求,并设置缓存过期时间和Cache-Control头部。

静态资源通常直接由Nginx处理,因此可以在location块中指定静态资源的目录。

使用正则表达式匹配静态资源的文件扩展名,如.jpg、.jpeg、.png、.gif、.ico、.css、.js等。

配置示例:

复制
server { listen 80; server_name www.example.com; # 静态资源处理 location /static/ { root /var/www/example; } location /images/ { root /var/www/example; } location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ { root /var/www/example; expires 30d; # 设置静态资源的缓存过期时间 add_header Cache-Control "public"; # 添加Cache-Control头部,强化浏览器缓存行为 } }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.

(2) 动态请求处理

对于动态请求,可以将请求转发给后端应用服务器(如PHP-FPM、Django、Node.js等)处理。

配置示例:

复制
server { listen 80; server_name www.example.com; # 动态请求处理 location ~ \.php$ { root /var/www/example; fastcgi_pass 127.0.0.1:9000; # 指定将这些请求转发给PHP-FPM服务器处理 fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } }1.2.3.4.5.6.7.8.9.10.11.12.13.

在上述配置中,location ~ \.php$使用正则表达式匹配所有以.php结尾的请求,并将这些请求视为动态请求。fastcgi_pass指定将这些请求转发给PHP-FPM服务器处理,127.0.0.1:9000是PHP-FPM的监听地址。其他参数用于设置FastCGI参数和请求的文件路径。

4. 注意事项

(1) Nginx的location匹配优先级

Nginx的location匹配遵循特定的优先级规则,包括精确匹配(使用=符号)、正则表达式匹配(使用~或~*)、前缀匹配(普通location)。在配置多个location块时,需要谨慎考虑它们之间的优先级关系,以确保正确的请求路由。

(2) 缓存策略

通过合理配置缓存,可以显著提高网站性能。Nginx提供了强大而灵活的缓存功能,可以通过proxy_cache_path和proxy_cache指令来实现。对于静态资源,可以设置较长的缓存过期时间,以减少对服务器的重复请求。

(3) 安全性

通过动静分离,可以将静态内容服务器与执行动态代码的服务器分离,从而降低安全风险。静态内容服务器通常不需要执行复杂的程序代码,因此攻击面较小。

面试官:Nginx负载均衡的算法策略有哪些?

1. 轮询(Round Robin)

原理:轮询算法按照服务器列表的顺序依次分发请求。当一个新的请求到达时,Nginx会将其分配给列表中的下一个服务器,如果到达列表末尾,则重新开始循环。

特点:

简单易用,无需额外配置。适用于后端服务器性能相近的情况,因为每个服务器都会轮流接收到请求。
复制
http { upstream backend { server backend1.example.com; server backend2.example.com; server backend3.example.com; } server { location / { proxy_pass http://backend; } } }1.2.3.4.5.6.7.8.9.10.11.12.
2. 加权轮询(Weighted Round Robin)

原理:在轮询的基础上,为每个服务器分配一个权重值。权重值越高的服务器,接收到的请求越多。Nginx会根据权重值来计算每个服务器接收请求的比例。

特点:

考虑了服务器性能的差异,可以灵活分配请求。需要手动配置权重值,以反映服务器的实际性能。适用于后端服务器性能不均衡的情况,可以更好地利用服务器资源。
复制
http { upstream backend { server backend1.example.com weight=3; server backend2.example.com weight=2; server backend3.example.com weight=1; } server { location / { proxy_pass http://backend; } } }1.2.3.4.5.6.7.8.9.10.11.12.
3. IP哈希(IP Hash)

原理:根据客户端IP地址的哈希值来分配请求。Nginx会计算每个客户端IP地址的哈希值,并使用该哈希值来选择后端服务器。由于相同IP地址的哈希值相同,因此来自同一IP地址的请求总是被分配到同一台后端服务器。

特点:

实现了会话粘性(Session Persistence),即同一个客户端的请求总是被分配到同一台后端服务器。适用于需要保持会话一致性的场景,如购物车、用户会话等。但可能导致负载分布不均衡,因为某些IP地址范围内的客户端可能会频繁访问同一台服务器。
复制
http { upstream backend { ip_hash; server backend1.example.com; server backend2.example.com; server backend3.example.com; } server { location / { proxy_pass http://backend; } } }1.2.3.4.5.6.7.8.9.10.11.12.13.
4. 最少连接(Least Connections)

原理:将请求分发到当前连接数最少的服务器上。Nginx会监控每台后端服务器的当前连接数,并将新请求分配给连接数最少的服务器。

特点:

考虑了服务器的当前负载情况,可以更有效地平衡负载。适用于长连接场景,如WebSocket、数据库连接等。但需要Nginx维护连接状态,可能会增加一些开销。Nginx本身不直接支持此策略,通常需要借助第三方模块或自定义脚本实现。
复制
http { upstream backend { least_conn; server backend1.example.com; server backend2.example.com; server backend3.example.com; } server { location / { proxy_pass http://backend; } } }1.2.3.4.5.6.7.8.9.10.11.12.13.
5. Fair(第三方)

原理:根据后端服务器的响应时间来分配请求。Nginx会监控每台后端服务器的响应时间,并将新请求分配给响应时间最短的服务器。

特点:

实现了更智能的负载均衡,可以根据服务器的实际性能来分配请求。适用于对响应时间要求较高的场景。但需要安装第三方模块(如nginx-module-vts)来实现。6. URL哈希(URL Hash,第三方)

原理:根据请求URL的哈希值来分配请求。Nginx会计算每个请求URL的哈希值,并使用该哈希值来选择后端服务器。由于相同URL的哈希值相同,因此相同URL的请求总是被分配到同一台后端服务器。

特点:

提高了缓存的命中率,因为相同URL的请求总是被分配到同一台后端服务器。适用于缓存服务器集群。但同样可能导致负载分布不均衡。Nginx本身不支持此策略,需要安装Nginx的hash软件包来实现。

THE END
本站服务器由亿华云赞助提供-企业级高防云服务器