面试官:说说看Nginx是如何处理请求的?为什么Nginx不采用多线程模型?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)
            # 其他可选配置,如缓存、超时、重试等
        }
    }
}

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

现实中的正向代理例子有: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;  # 设置请求头
    }
}

以上配置中,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头部,强化浏览器缓存行为
	}
}

(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;
	}
}

在上述配置中,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;
        }
    }
}

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;
        }
    }
}

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;
        }
    }
}

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;
        }
    }
}

5. Fair(第三方)

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

特点:

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

6. URL哈希(URL Hash,第三方)

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

特点:

  • 提高了缓存的命中率,因为相同URL的请求总是被分配到同一台后端服务器。
  • 适用于缓存服务器集群。
  • 但同样可能导致负载分布不均衡。
  • Nginx本身不支持此策略,需要安装Nginx的hash软件包来实现。
责任编辑:赵宁宁 来源: 程序员阿沛
相关推荐

2022-07-01 07:33:24

nginx反向代理测试

2017-12-18 12:04:02

Nginx代理均衡

2018-11-12 12:17:00

2021-03-16 21:42:37

反向代理正向代理

2023-12-05 09:14:54

2024-07-22 15:34:25

2012-12-07 10:14:48

Nginx负载均衡

2015-06-05 11:26:58

nginx运维

2019-11-04 15:35:53

Nginx反向代理负载均衡

2020-07-28 15:10:34

Nginx反向代理负载均衡

2019-06-19 15:34:39

Nginx反向代理负载均衡

2019-09-18 10:39:08

负载均衡反向代理TCP

2018-11-05 09:34:43

2021-01-18 05:13:04

TomcatHttp

2020-10-22 08:05:46

Nginx

2023-02-20 08:27:17

2021-04-19 18:56:58

大数字符串运算

2022-06-10 13:56:42

Java

2024-02-01 08:32:03

Nginx服务器代理

2020-09-07 06:28:37

Nginx静态负载均衡动态负载均衡
点赞
收藏

51CTO技术栈公众号