服务端5月30日 15:53
什么是 Nginx?它为什么适合高并发?Nginx 是一个高性能 Web 服务器,也常用作反向代理、负载均衡器、静态资源服务器和 API 网关。它适合高并发,核心原因是事件驱动和异步非阻塞 I/O:少量 worker 进程通过事件循环处理大量连接,不需要给每个连接都分配一个线程。简单说,Apache 更像“一个请求安排一个人盯着”,Nginx 更像“少量工作人员按事件通知处理”,长连接和静态资源场景下资源消耗更低。
## 追问
### Nginx 主要解决什么问题?
它常解决三件事:对外提供 HTTP 服务,把请求转发给后端,以及把流量分摊到多台服务器。实际项目里还会让它处理 HTTPS 终止、静态资源缓存、限流、压缩、访问日志和灰度路由。
### 为什么说 Nginx 适合高并发?
它不为每个连接创建独立线程,而是通过 worker 进程和事件循环处理连接状态变化。大量连接处于等待网络 I/O 时,Nginx 可以把 CPU 留给真正有事件的连接,所以并发连接数上来后仍然比较稳。
### Nginx 和 Apache 有什么区别?
Nginx 更擅长高并发连接、反向代理和静态资源;Apache 模块生态成熟,动态内容处理能力强。现代架构里常见做法是 Nginx 放入口层,业务逻辑交给 Node.js、Java、Go、PHP-FPM 等后端服务。
### 反向代理和负载均衡分别是什么?
反向代理是客户端只访问 Nginx,由 Nginx 转发到真实后端;负载均衡是在多个后端实例之间分配请求。两者经常一起用:Nginx 既隐藏后端地址,也按权重、轮询、IP Hash 等策略分流。
### 实际项目里怎么用?
常见链路是“用户 → CDN/云负载均衡 → Nginx → 应用服务”。Nginx 负责入口层的 TLS、路由、缓存、限流和日志;后端只关注业务处理。排查问题时也通常先看 Nginx access.log、error.log 和 upstream 响应时间。
## 写段配置
```nginx
upstream app {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
}
server {
listen 80;
server_name example.com;
location /static/ {
root /var/www;
expires 7d;
}
location /api/ {
proxy_pass http://app;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
```标签
Nginx
Nginx 是一个网络和代理服务器。Nginx (发音为 "engine-x") 是一个高性能的 HTTP 和反向代理服务器,同时也是一个 IMAP/POP3 代理服务器。Nginx 是由 Igor Sysoev 开发的,最初发布于2004年,旨在解决 C10k 问题,即同时处理大量客户端连接的需求。由于其高性能、稳定性、丰富的功能集以及低资源消耗,Nginx 在全球范围内广泛用于提供网页内容,特别是在高流量的网站中非常流行。

服务端5月30日 12:39
Nginx 常见故障有哪些?如何快速排查?Nginx 常见故障主要看三类:状态码、连接和性能。502 多半是后端不可用或 upstream 配错;504 是后端响应太慢;403 是权限、目录索引或访问控制问题;404 看 root、alias、try_files;413 调 client_max_body_size;并发打满就查 worker_connections、文件描述符和后端连接池。排查顺序:先确认现象,再看 error.log,再跑 nginx -t,最后查后端和系统资源。
## 追问
### 502 和 504 有什么区别?
502 是 Nginx 连不上或拿不到有效后端响应;504 是连上了但后端超时。
### 403 一定是文件权限问题吗?
不一定。还可能是目录没有 index、allow/deny 命中、SELinux 拦截,或 root/alias 写错。
### 响应慢怎么定位?
access log 记录 `$request_time` 和 `$upstream_response_time`,区分慢在客户端、Nginx、网络还是后端。服务端5月30日 12:39
Nginx 常见部署架构有哪些?如何选择?Nginx 常见部署架构主要有单机、反向代理、负载均衡、多层代理、CDN 源站、高可用、缓存层和 API 网关。选择时先看业务规模:小站用单机;多应用统一入口用反向代理;高并发用负载均衡;怕单点故障用 Keepalived 或云负载均衡;全球访问配 CDN;读多写少加缓存。别一开始就堆复杂架构,出问题时没人知道流量走哪一层。
## 追问
### 反向代理和负载均衡有什么区别?
反向代理强调统一入口和转发,负载均衡强调把流量分给多台后端。实际项目里两者通常一起用。
### 高可用架构怎么做?
常见做法是两台 Nginx 配 Keepalived 共享 VIP,主节点故障后 VIP 漂移到备节点。云上更常用 SLB/ELB。
## 写段配置
```nginx
upstream backend { server 10.0.0.11:8080; server 10.0.0.12:8080; }
location / { proxy_pass http://backend; }
```服务端5月28日 08:25
Nginx 如何优化静态资源?有哪些优化策略?## Nginx 如何优化静态资源?有哪些优化策略?
Nginx 处理静态资源的能力是面试高频考点。优化的核心思路是:减少磁盘 I/O、压缩传输体积、利用缓存避免重复请求、将负载推到边缘节点。下面从内核层到架构层逐级展开。
### sendfile 与零拷贝:从内核直接发送
传统文件读取流程:磁盘 → 内核缓冲区 → 用户空间 → Socket 缓冲区 → 网卡,经历两次用户态拷贝。`sendfile` 让数据直接在内核态完成传输,省掉这两次拷贝,这是 Nginx 静态服务高性能的底层基础。
```nginx
http {
sendfile on; # 启用零拷贝,数据在内核态直接从文件描述符传输到 socket
tcp_nopush on; # 在包头积累到最大后才发送,减少网络帧数
tcp_nodelay on; # 禁用 Nagle 算法,小包立即发送
}
```
**面试追问:tcp_nopush 和 tcp_nodelay 看起来矛盾,为什么要同时开启?**
tcp_nopush 在 sendfile 阶段生效:把 HTTP 响应头和文件内容拼成一个大数据块再发送,减少碎片帧;当最后一块数据不足 MSS 时,tcp_nodelay 接管,确保尾部数据不延迟发出。两者作用阶段不同,互不冲突。这是 Nginx 面试的经典追问。
### Gzip 压缩:减小传输体积
文本类资源(HTML/CSS/JS/JSON/SVG)压缩收益显著,通常可减少 60%-80% 体积。但图片和视频本身已是压缩格式,再做 Gzip 反而浪费 CPU 且体积几乎不变。
```nginx
http {
gzip on;
gzip_vary on; # 添加 Vary: Accept-Encoding,帮助 CDN 区分压缩/非压缩版本
gzip_min_length 1024; # 小于 1KB 不压缩,压缩收益抵不过开销
gzip_comp_level 6; # 压缩级别 1-9,6 是速度与压缩率的最佳平衡点
gzip_types text/plain text/css text/javascript
application/json application/javascript
application/xml image/svg+xml;
gzip_static on; # 优先发送预压缩的 .gz 文件,避免实时压缩消耗 CPU
}
```
**gzip_static 的意义:** 生产环境建议在构建阶段预生成 `.gz` 文件,Nginx 直接发送预压缩文件,零 CPU 开销。`gzip on` 作为兜底,当 `.gz` 文件不存在时实时压缩。
### 浏览器缓存:避免重复请求
缓存策略的核心是根据资源更新频率设置不同的过期时间。带哈希的静态资源(如 `app.a3b2c1.js`)可以长期缓存并标记 `immutable`,HTML 入口文件则需要短缓存或必须重新验证。
```nginx
server {
# 带哈希的静态资源:长期缓存 + immutable
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off; # 静态资源不写日志,减少磁盘 I/O
}
# HTML 文件:短期缓存,必须重新验证
location ~* \.html$ {
expires 1h;
add_header Cache-Control "public, must-revalidate";
}
}
```
**面试追问:immutable 是什么意思?**
告诉浏览器,资源在过期前不会变化,不需要发条件请求(If-Modified-Since / If-None-Match)验证。配合文件名哈希使用,用户刷新页面也不会产生 304 请求,直接从本地缓存读取。这在 HTTP RFC 8246 中被标准化。
### 文件描述符缓存:减少系统调用
Nginx 对频繁访问的文件可以缓存文件描述符(fd),避免每次请求都执行 `open()` / `stat()` 系统调用。在高并发场景下,这个优化效果显著。
```nginx
http {
open_file_cache max=10000 inactive=20s;
open_file_cache_valid 30s; # 每 30s 验证缓存项是否仍有效(检查文件是否被修改)
open_file_cache_min_uses 2; # 20s 内访问少于 2 次则移除,避免冷文件占用缓存
open_file_cache_errors on; # 缓存文件不存在等错误,防止同一 404 反复穿透磁盘
}
```
内存开销:每个缓存项约占 256 字节,10000 项约 2.5MB,对现代服务器可忽略。但 `open_file_cache_errors on` 要注意,如果频繁请求不存在的文件,错误缓存会占用大量条目。
### 动静分离与 CDN
将静态资源部署到独立域名或 CDN 有三个核心收益:
1. **突破浏览器同域并发限制**——浏览器对同一域名通常限制 6 个并发连接,独立域名可以并行下载更多资源
2. **减少主站 Cookie 污染**——静态资源请求不携带主站 Cookie,减少请求体积
3. **边缘节点就近分发**——CDN 将资源缓存到离用户最近的节点,大幅降低延迟
```nginx
server {
listen 80;
server_name example.com;
# 方案一:静态资源独立目录
location /static/ {
root /var/www/static;
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# 方案二:直接重写到 CDN
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff2)$ {
return 301 https://cdn.example.com$request_uri;
}
}
```
### HTTP/2 与资源预加载
HTTP/2 多路复用已大幅降低多请求的开销,但资源预加载仍然有效。需要注意:Chrome 106+ 已移除 HTTP/2 Server Push 支持,推荐使用 `<link rel="preload">` 替代。
```nginx
server {
listen 443 ssl http2;
# 使用 Link header 预加载关键资源
location = / {
add_header Link "</css/style.css>; rel=preload; as=style, </js/app.js>; rel=preload; as=script";
}
}
```
**preload vs prefetch:** `preload` 告诉浏览器当前页面一定会用到该资源,立即下载;`prefetch` 表示下一页可能用到,空闲时下载。面试中常考两者区别。
### 图片与字体优化
```nginx
server {
# WebP 自动转换:浏览器支持 WebP 时优先返回
location ~* \.(jpg|png)$ {
try_files $uri$webp_suffix $uri =404;
expires 1y;
add_header Cache-Control "public, immutable";
add_header Vary Accept; # 告诉 CDN 根据 Accept 头缓存不同版本
}
# 字体文件:CORS 支持 + 长缓存
location ~* \.(woff2?|ttf|otf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
add_header Access-Control-Allow-Origin "*";
access_log off;
}
# 图片防盗链
location ~* \.(jpg|jpeg|png|gif)$ {
valid_referers none blocked example.com *.example.com;
if ($invalid_referer) {
return 403;
}
}
}
```
WebP 相比 JPEG/PNG 通常可减少 25%-35% 体积,配合 CDN 的 `Vary: Accept` 头可以同时缓存原始格式和 WebP 格式。
### 静态资源合并
CSS/JS 合并减少请求数,但在 HTTP/2 场景下收益降低。是否合并需根据实际场景权衡:
- HTTP/1.1 环境:合并有效,减少连接开销
- HTTP/2 环境:多路复用已解决队头阻塞,合并收益有限,反而影响缓存命中率
```nginx
# 使用 ngx_http_concat_module(淘宝开源模块)
# 访问 /static/css/??a.css,b.css,c.css 可合并返回
location /static/css/ {
concat on;
concat_types text/css;
concat_max_files 10;
}
```
### 安全与日志优化
```nginx
server {
# 禁止访问隐藏文件(.git、.env 等敏感文件)
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# 静态资源关闭 access_log,减少磁盘写入
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff2?|ttf|eot)$ {
access_log off;
}
}
```
隐藏文件泄露是常见的安全漏洞。`.git` 目录暴露可能导致源码泄露,`.env` 暴露可能泄露数据库密码和 API Key。
### 生产环境完整配置参考
```nginx
user nginx;
worker_processes auto; # 自动匹配 CPU 核心数
worker_rlimit_nofile 65535; # 提升进程级文件描述符上限
events {
worker_connections 10240; # 每个 worker 的最大连接数
use epoll; # Linux 下使用 epoll 事件模型
multi_accept on; # 一次性接受所有新连接
}
http {
# 内核层优化
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# 传输层优化
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_types text/plain text/css text/javascript
application/json application/javascript
application/xml image/svg+xml;
gzip_static on;
# 文件系统缓存
open_file_cache max=10000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
# 按内容类型动态设置缓存时间
map $sent_http_content_type $expires {
default off;
text/html 1h;
text/css 1y;
application/javascript 1y;
~image/ 1y;
~font/ 1y;
}
server {
listen 80;
server_name example.com;
root /var/www/html;
# 静态资源长缓存
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff2?|ttf|eot)$ {
expires $expires;
add_header Cache-Control "public, immutable";
access_log off;
}
# 字体 CORS
location ~* \.(woff2?|ttf|otf|eot)$ {
add_header Access-Control-Allow-Origin "*";
}
# 禁止隐藏文件
location ~ /\. {
deny all;
}
location / {
try_files $uri $uri/ =404;
}
}
}
```
### 优化策略速查表
| 层级 | 策略 | 核心指令 | 收益 |
|------|------|----------|------|
| 内核层 | 零拷贝传输 | sendfile + tcp_nopush + tcp_nodelay | 减少 CPU 拷贝和网络碎片帧 |
| 传输层 | Gzip 压缩 | gzip + gzip_static | 文本资源体积减少 60%-80% |
| 缓存层 | 浏览器缓存 | expires + Cache-Control immutable | 避免重复请求,零带宽消耗 |
| 缓存层 | 文件描述符缓存 | open_file_cache | 减少 open/stat 系统调用 |
| 架构层 | 动静分离 + CDN | 独立域名 + CDN 回源 | 突破并发限制 + 就近分发 |
| 协议层 | HTTP/2 + Preload | http2 + Link header | 多路复用 + 关键资源预加载 |
| 格式层 | WebP + WOFF2 | try_files + Vary Accept | 图片体积再减 25%-35% |
| 安全层 | 防盗链 + 隐藏文件 | valid_referers + deny | 防止资源盗用和源码泄露 |
面试中回答这类问题,建议按"内核优化 → 传输优化 → 缓存策略 → 架构设计"的层次递进,体现系统思维。每个策略说清原理、配置和收益,比单纯罗列配置更有说服力。如果面试官追问某一项的细节,可以深入到内核原理层面(如 sendfile 的 DMA 实现、epoll 的 LT/ET 模式),展示技术深度。服务端5月28日 08:23
Nginx 如何处理动态内容?有哪些配置方式?## Nginx 如何处理动态内容?有哪些配置方式?
Nginx 本身不执行动态脚本,它的角色是**请求分发器**——根据协议类型将动态请求转给后端应用服务器处理,自己只负责连接管理、缓冲、压缩和缓存等"外围工作"。常见的转发协议有四种:FastCGI、uWSGI、SCGI 和 HTTP 反向代理。
## 核心答案:四种协议的区别与选型
| 协议 | 典型后端 | 通信方式 | 适用场景 |
|------|---------|---------|---------|
| FastCGI | PHP-FPM | Unix socket / TCP | PHP 项目,最常见 |
| uWSGI | Python (Django/Flask) | Unix socket / TCP | Python Web 应用 |
| SCGI | Ruby 等 | Unix socket / TCP | 较少使用 |
| HTTP Proxy | Node.js / Go / Java | HTTP/TCP | 通用,任何 HTTP 服务 |
**选型原则**:后端用什么语言/框架,就用对应协议。如果后端本身就是 HTTP 服务(如 Node.js),直接用 `proxy_pass` 做反向代理即可。
## FastCGI 配置(PHP 项目最常用)
FastCGI 是 Nginx 处理 PHP 的标准方式。关键指令是 `fastcgi_pass`,它告诉 Nginx 把请求转发到哪里。
```nginx
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
fastcgi_index index.php;
# 这个参数必须设置,否则 PHP 找不到要执行的脚本
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
# 超时:后端响应慢时防止 Nginx 一直等
fastcgi_connect_timeout 60s;
fastcgi_send_timeout 60s;
fastcgi_read_timeout 60s;
# 缓冲区:后端返回内容先缓冲再发给客户端
fastcgi_buffer_size 4k;
fastcgi_buffers 8 4k;
}
```
**几个容易踩的坑**:
- `SCRIPT_FILENAME` 必须拼成绝对路径,否则 PHP-FPM 报 404
- Unix socket 比 TCP 快(省掉网络栈开销),但只能本机通信
- `try_files $uri =404` 防止 PHP 执行上传目录里的恶意脚本
## uWSGI 配置(Python 项目)
Django、Flask 等 Python 框架常用 uWSGI 部署,Nginx 通过 `uwsgi_pass` 转发请求。
```nginx
upstream django_backend {
server unix:/var/run/uwsgi/app.sock;
server 127.0.0.1:8000;
}
server {
listen 80;
server_name example.com;
location / {
uwsgi_pass django_backend;
include uwsgi_params;
uwsgi_connect_timeout 60s;
uwsgi_read_timeout 60s;
}
# 静态文件直接由 Nginx 处理,不走 uWSGI
location /static/ {
alias /var/www/html/static/;
expires 1y;
}
}
```
**核心区别**:uWSGI 用自己的二进制协议,比 HTTP 更紧凑高效,但只有 Nginx 等 Web 服务器能直接对接。
## HTTP 反向代理(Node.js / Go / Java)
如果后端本身就是一个 HTTP 服务,直接用 `proxy_pass` 转发,这也是微服务架构中最常见的方式。
```nginx
upstream nodejs_backend {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://nodejs_backend;
# 透传客户端真实信息
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# WebSocket 支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
```
**为什么需要这些 header**:后端拿到的 remote_addr 是 Nginx 的 IP 而非客户端 IP,必须通过 `X-Real-IP` 和 `X-Forwarded-For` 透传。WebSocket 升级也需要额外的 `Upgrade` 头。
## FastCGI 缓存:让动态内容也变快
不是所有动态请求都要实时回源。比如文章详情页、列表页这类"准静态"内容,可以用 FastCGI 缓存大幅降低后端压力。
```nginx
# 在 http 块中定义缓存区
fastcgi_cache_path /var/cache/nginx/fastcgi levels=1:2
keys_zone=fastcgi_cache:10m max_size=1g inactive=60m;
server {
location ~ \.php$ {
fastcgi_cache fastcgi_cache;
fastcgi_cache_valid 200 60m; # 200 响应缓存 60 分钟
fastcgi_cache_valid 404 1m; # 404 只缓存 1 分钟
fastcgi_cache_key "$scheme$request_method$host$request_uri";
# 登录用户等场景需要跳过缓存
set $skip_cache 0;
if ($http_cookie ~* "comment_author|wordpress_logged_in") {
set $skip_cache 1;
}
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
add_header X-Cache-Status $upstream_cache_status;
}
}
```
**缓存清理**:`inactive=60m` 表示 60 分钟无访问自动清除。如果需要主动清除,可用 `proxy_cache_purge` 模块或直接删除缓存目录下的文件。
## 动态内容负载均衡
后端多实例部署时,Nginx 的 upstream 模块提供多种分发策略:
```nginx
upstream php_backend {
least_conn; # 最少连接数优先
server 192.168.1.100:9000 weight=3; # 权重 3
server 192.168.1.101:9000 weight=2; # 权重 2
server 192.168.1.102:9000; # 默认权重 1
keepalive 32; # 保持长连接,减少握手开销
}
```
**策略选择**:
- `round-robin`(默认):轮询,适合后端性能一致的场景
- `least_conn`:最少连接优先,适合请求处理时间差异大的场景
- `ip_hash`:同一 IP 固定到同一后端,适合需要会话保持的场景
## Gzip 压缩:减少传输体积
动态内容通常是 JSON/HTML,压缩率很高,开启 Gzip 可以减少 60%-80% 的传输量。
```nginx
gzip on;
gzip_vary on;
gzip_min_length 1024; # 小于 1KB 不压缩,压缩反而更大
gzip_comp_level 6; # 6 是性能和压缩率的平衡点
gzip_types text/plain text/css application/json
application/javascript application/xml;
```
## 安全配置要点
动态内容处理中,安全问题主要集中在两个方向:防止恶意脚本执行和限制请求大小。
```nginx
# 禁止访问敏感文件
location ~* \.(htaccess|htpasswd|ini|log|sh|sql|bak|swp)$ {
deny all;
}
# 防止上传目录中的 PHP 被执行
location ^~ /uploads/ {
location ~ \.php$ {
deny all;
}
}
# 限制请求体大小
client_max_body_size 10m;
```
**常见攻击面**:上传一个 `.php` 文件到图片目录,然后直接访问执行。用 `^~` 前缀匹配 + 内部 deny 可以彻底堵住这个口。
## 追问:Nginx 处理动态内容的请求流程是什么?
客户端请求到达 Nginx 后,大致经历以下步骤:
1. Nginx 接收请求,根据 `location` 规则匹配到对应的转发配置
2. 按协议(FastCGI/uWSGI/HTTP)将请求转发给后端应用服务器
3. 后端处理完成,将响应返回给 Nginx
4. Nginx 对响应做缓冲、压缩、缓存等处理
5. 最终将响应返回给客户端
**关键点**:Nginx 在整个过程中只做"搬运工",不解析脚本内容。缓冲机制确保即使客户端读得慢,后端也能尽快释放连接;缓存机制则让重复请求直接从 Nginx 返回,后端完全不用参与。
## 追问:502 Bad Gateway 和 504 Gateway Timeout 有什么区别?
- **502**:Nginx 成功连接到后端,但后端返回了无效响应(进程崩溃、端口未监听、协议不匹配等)
- **504**:Nginx 等待后端响应超时(后端处理太慢,超过了 `fastcgi_read_timeout` 或 `proxy_read_timeout`)
排查思路:先看后端进程是否存活,再看 Nginx error log 中的具体错误信息,最后根据超时或连接失败调整对应参数。服务端5月28日 07:28
Nginx 如何配置虚拟主机?有哪些配置方式?## Nginx 如何配置虚拟主机?有哪些配置方式?
虚拟主机(Virtual Host)是 Nginx 最核心的能力之一——一台服务器、一个 Nginx 进程,就能同时服务几十个甚至上百个网站。面试中这道题考察的不只是"怎么配",更是你对其背后路由机制的理解深度。
Nginx 虚拟主机的本质就是 `server` 块。每个 `server` 块是一个独立的虚拟主机,Nginx 根据请求的域名、端口或 IP 地址,将请求路由到匹配的 `server` 块处理。三个关键指令决定了路由规则:
- `listen`:监听的地址和端口,如 `listen 80` 或 `listen 443 ssl`
- `server_name`:匹配请求头中的 `Host` 字段,支持精确匹配、通配符和正则
- `root`:该虚拟主机的网站根目录
### 基于域名的虚拟主机
**这是生产环境最主流的方式。** 多个域名共享同一个 IP,Nginx 根据 HTTP 请求头中的 `Host` 字段决定将请求交给哪个 `server` 块处理。这也叫 Name-Based Virtual Host:
```nginx
server {
listen 80;
server_name example.com www.example.com;
root /var/www/example.com;
index index.html;
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
location / {
try_files $uri $uri/ =404;
}
}
server {
listen 80;
server_name test.com www.test.com;
root /var/www/test.com;
index index.html;
access_log /var/log/nginx/test.com.access.log;
error_log /var/log/nginx/test.com.error.log;
location / {
try_files $uri $uri/ =404;
}
}
```
两个 `server` 块都监听 80 端口,Nginx 收到请求后先匹配 `server_name`,命中哪个就走哪个配置。`server_name` 用空格分隔可以写多个域名,`www.example.com` 和 `example.com` 都会命中第一个块。
**面试追问:如果两个 server 块的 server_name 都匹配同一个域名会怎样?** Nginx 有明确的匹配优先级:精确匹配 > 最长通配符前缀 > 最长通配符后缀 > 第一个正则匹配 > default_server。优先级相同则按配置文件加载顺序,先加载的优先。
### 基于端口的虚拟主机
通过不同端口区分服务,适合将管理后台、API 服务与主站做物理隔离:
```nginx
# 主站
server {
listen 80;
server_name example.com;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
# 管理后台
server {
listen 8080;
server_name example.com;
root /var/www/example.com/admin;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
# HTTPS 安全服务
server {
listen 8443 ssl;
server_name example.com;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
root /var/www/example.com/secure;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
```
端口方式的缺点是用户需要显式指定端口号(如 `example.com:8080`),80 和 443 之外的端口对用户不友好,因此一般只用于内部服务或管理入口。
### 基于 IP 地址的虚拟主机
服务器绑定多个 IP 时,按 IP 地址区分站点。这种方式在早期互联网常用,但在现代云环境下已很少使用——公网 IP 资源有限且费用高,基于域名的方式更经济:
```nginx
server {
listen 192.168.1.100:80;
server_name example.com;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
server {
listen 192.168.1.101:80;
server_name example.com;
root /var/www/example.com/mirror;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
```
### 通配符与正则域名匹配
子域名多且动态变化时,逐一配置 `server_name` 不现实。Nginx 提供通配符和正则两种高级匹配:
**通配符匹配**——用 `*` 匹配子域名:
```nginx
server {
listen 80;
server_name *.example.com;
location / {
set $subdomain $host;
if ($subdomain ~* ^(.*)\.example\.com$) {
set $subdomain $1;
}
root /var/www/subdomains/$subdomain;
index index.html;
}
}
# 默认虚拟主机:兜底处理未匹配的请求
server {
listen 80 default_server;
server_name _;
root /var/www/default;
return 444; # 直接关闭连接,比 404 更安全
}
```
`default_server` 是安全防线——任何未匹配到 `server_name` 的请求都会走这个块。建议返回 444(Nginx 特有状态码,直接关闭连接),比返回 404 更能防止信息泄露。
**正则表达式匹配**——用命名捕获组提取子域名:
```nginx
server {
listen 80;
server_name ~^(?<subdomain>.+)\.example\.com$;
root /var/www/example.com/$subdomain;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
server {
listen 80;
server_name ~^(?<user>.+)\.users\.example\.com$;
root /var/www/users/$user;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
```
`?<name>` 是命名捕获组,提取的值以 `$name` 变量在配置中引用。通配符只能做简单的 `*` 匹配,正则则能处理复杂的域名模式——实际项目中正则用得更多。
### HTTPS 虚拟主机
生产环境必须启用 HTTPS,标准做法是 HTTP 301 永久跳转到 HTTPS:
```nginx
# HTTP -> HTTPS 301 跳转
server {
listen 80;
server_name example.com;
return 301 https://$server_name$request_uri;
}
# HTTPS 服务
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
root /var/www/example.com;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
```
`http2` 参数直接在 `listen` 指令中启用 HTTP/2,无需额外编译模块。`ssl_session_cache` 将 SSL 会话缓存 10 分钟,客户端重连时可以复用,避免完整的 TLS 握手,显著提升 HTTPS 性能。SSL 协议只保留 TLSv1.2 和 TLSv1.3,禁用所有不安全的旧版本。
### 反向代理虚拟主机
虚拟主机在生产中最常见的用途是反向代理——不同域名转发到不同的后端服务,Nginx 作为网关统一入口:
```nginx
upstream backend1 {
server 192.168.1.100:8080;
server 192.168.1.101:8080;
}
upstream backend2 {
server 192.168.1.200:8080;
server 192.168.1.201:8080;
}
server {
listen 80;
server_name api.example.com;
location / {
proxy_pass http://backend1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
server {
listen 80;
server_name admin.example.com;
location / {
proxy_pass http://backend2;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
```
`proxy_set_header` 三件套必须配置:`Host` 让后端知道原始域名,`X-Real-IP` 传递客户端真实 IP,`X-Forwarded-For` 追加代理链路。不加这些头,后端拿到的全是 Nginx 的内网 IP,日志和鉴权都会出问题。
### 多域名共享配置
多个域名配置相似、仅 `root` 路径不同时,用 `map` 指令消除重复:
```nginx
map $host $root_path {
example.com /var/www/example.com;
test.com /var/www/test.com;
default /var/www/default;
}
server {
listen 80;
server_name example.com test.com;
root $root_path;
index index.html;
location / {
try_files $uri $uri/ =404;
}
}
```
`map` 在请求处理阶段根据 `$host` 变量的值映射到对应目录,一个 `server` 块就能服务多个域名。当域名数量超过 5 个且配置差异仅在路径时,这种方式比逐个写 `server` 块更易维护。
### 配置文件分离与站点管理
生产环境中,千万不要把所有虚拟主机堆在 `nginx.conf` 一个文件里。用 `include` 拆分:
```nginx
# /etc/nginx/nginx.conf
http {
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
```
每个虚拟主机一个配置文件,放在 `sites-available` 目录,通过符号链接启用:
```bash
# 启用站点
ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
# 禁用站点
rm /etc/nginx/sites-enabled/example.com
# 测试配置语法(修改后必做)
nginx -t
# 重新加载配置(不影响正在处理的请求)
nginx -s reload
```
`nginx -t` 是生产操作的铁律——修改配置后先检测语法,通过后再 `reload`。`reload` 是平滑重载,Nginx 会等旧请求处理完再切换到新配置,不会中断服务。而 `restart` 会直接杀掉工作进程,正在处理的请求会断开。
### PHP 应用虚拟主机
WordPress、Laravel 等 PHP 应用需要配置 FastCGI 传递给 PHP-FPM:
```nginx
server {
listen 80;
server_name example.com;
root /var/www/example.com;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
}
```
`try_files` 中的 `/index.php?$query_string` 是 PHP 框架的标配写法——先尝试找静态文件,找不到就转发给 `index.php` 处理,这是 Laravel 等框架 URL 重写的基础。`SCRIPT_FILENAME` 必须用 `$document_root` 拼接,否则 PHP-FPM 找不到脚本文件。
### 静态站点虚拟主机
纯静态站点可以做更激进的优化——关日志、开压缩、长缓存:
```nginx
server {
listen 80;
server_name static.example.com;
root /var/www/static;
index index.html;
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript;
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
location / {
try_files $uri $uri/ =404;
}
}
```
静态资源关闭 `access_log` 能显著减少磁盘 IO。高流量站点中,日志写入是主要瓶颈之一,对不需要统计的静态资源关闭日志是常规优化手段。
### 生产环境完整配置模板
综合安全、性能和可维护性的完整配置,可作为新项目的起点:
```nginx
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# SSL
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
# 站点根目录
root /var/www/example.com;
index index.php index.html;
# 日志
access_log /var/log/nginx/example.com.access.log;
error_log /var/log/nginx/example.com.error.log;
# 安全头
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/json application/javascript;
# 静态资源长缓存
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# PHP-FPM
location ~ \.php$ {
fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# 主路由
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# 禁止访问隐藏文件(.git、.env 等)
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
}
```
### 配置要点总结
**三种类型的选用原则**:基于域名是首选方案,一个 IP 托管所有站点,配置简单且用户无感知;基于端口适合内部服务隔离,但需要用户感知端口;基于 IP 在公网环境下已基本淘汰。
**`server_name` 匹配优先级**(面试必考):精确匹配 > 通配符前缀(`*.example.com`)> 通配符后缀(`example.*`)> 正则表达式 > `default_server`。优先级相同的,按配置文件加载顺序决定。
**配置排错四步法**:`nginx -t` 检查语法 -> `nginx -s reload` 确认重载 -> 检查 `server_name` 是否匹配请求域名 -> 查看 `error.log` 中的具体报错。大部分"配置不生效"的问题,要么是忘记 `reload`,要么是 `server_name` 写错了。服务端5月28日 07:26
Nginx 的 location 指令如何匹配?优先级是什么?## Nginx 的 location 指令如何匹配?优先级是什么?
location 是 Nginx 中最核心的指令之一,它决定了一个请求由哪个配置块来处理。理解它的匹配规则和优先级,不仅是面试高频考点,更是排查 Nginx 配置问题的基本功。
### location 的四种匹配方式
location 指令的语法:`location [=|~|~*|^~] uri { ... }`
修饰符不同,匹配行为完全不同:
| 修饰符 | 匹配方式 | 匹配后是否继续搜索 |
|--------|---------|-------------------|
| `=` | 精确匹配 | 否,立即停止 |
| `^~` | 前缀匹配 | 否,跳过正则检查 |
| `~` | 正则匹配(区分大小写) | 否,按配置顺序首个命中即停止 |
| `~*` | 正则匹配(不区分大小写) | 否,按配置顺序首个命中即停止 |
| 无 | 前缀匹配 | 是,继续检查正则 |
#### 精确匹配(=)
只有请求 URI 与指定路径完全一致时才命中,一旦匹配立即停止搜索,性能最优:
```nginx
location = / {
# 仅匹配 /,不匹配 /index.html
}
```
#### 前缀匹配(无修饰符)
按 URI 前缀匹配,匹配成功后不会立即使用,而是先记住这个最长前缀匹配,继续检查正则表达式。如果正则没有命中,才会回退使用这个前缀匹配:
```nginx
location /docs/ {
# 匹配以 /docs/ 开头的所有 URI
# 但如果有正则也命中了,正则优先
}
```
#### 正则匹配(~ 和 ~*)
- `~`:区分大小写
- `~*`:不区分大小写
正则匹配按配置文件中出现的顺序依次检查,首个命中即停止:
```nginx
location ~ \.php$ {
# 区分大小写,匹配 .php 结尾的请求
}
location ~* \.(jpg|png|gif|css|js)$ {
# 不区分大小写,匹配常见静态资源
}
```
此外还有 `!~` 和 `!~*`,表示正则不匹配,但它们不能用于 location 指令,只能用在 if 条件判断中。
#### 前缀匹配(^~)
行为与无修饰符的前缀匹配类似,但关键区别是:如果 `^~` 前缀匹配成功,会跳过后续所有正则检查,直接使用该 location。这在对性能敏感的场景下非常有用:
```nginx
location ^~ /static/ {
# 匹配 /static/ 开头的请求
# 即使有正则也匹配,也不检查,直接用这个
}
```
### Nginx 完整匹配算法
面试中光背优先级顺序不够,必须理解 Nginx 的完整匹配流程:
1. Nginx 首先检查所有前缀匹配(包括 `=`、`^~` 和无修饰符),找到最长前缀匹配
2. 如果最长前缀是精确匹配(`=`),立即使用,匹配结束
3. 如果最长前缀是 `^~` 匹配,立即使用,匹配结束
4. 按配置文件顺序依次检查所有正则表达式(`~` 和 `~*`)
5. 如果正则命中,使用该正则 location,匹配结束
6. 如果所有正则都未命中,回退使用步骤 1 中找到的最长前缀匹配
这个流程说明一个关键点:**正则匹配的优先级高于普通前缀匹配,但低于 `=` 和 `^~`**。
### 优先级总结(从高到低)
1. **精确匹配 `=`** — 最高优先级,匹配即停
2. **前缀匹配 `^~`** — 匹配后跳过正则检查
3. **正则匹配 `~` / `~*`** — 按配置顺序,先到先得
4. **普通前缀匹配** — 优先级最低,作为兜底
注意:正则匹配之间没有优先级之分,完全取决于配置文件中的书写顺序,写在前面的先匹配。
### 配置示例与匹配结果
```nginx
server {
listen 80;
server_name example.com;
location = / {
return 200 "1: exact /";
}
location / {
return 200 "5: prefix /";
}
location ^~ /images/ {
return 200 "2: ^~ /images/";
}
location ~ \.php$ {
return 200 "3: regex .php";
}
location ~* \.(jpg|png|gif)$ {
return 200 "4: regex image";
}
location /docs/ {
return 200 "6: prefix /docs/";
}
}
```
匹配结果验证:
| 请求 URI | 命中 location | 原因 |
|----------|--------------|------|
| `/` | `= /` | 精确匹配,优先级最高 |
| `/images/logo.jpg` | `^~ /images/` | `^~` 命中后跳过正则 |
| `/api/test.php` | `~ \.php$` | 前缀匹配 `/` 记住后,正则命中 |
| `/photo.JPG` | `~* \.(jpg\|png\|gif)$` | 不区分大小写正则命中 |
| `/docs/readme.html` | `/docs/` | 最长前缀 `/docs/` 优先于 `/`,且无正则命中 |
| `/about` | `/` | 仅前缀 `/` 命中,无正则匹配 |
### 常见面试陷阱
**陷阱一:前缀匹配的长度优先**
多个普通前缀同时匹配时,Nginx 选择最长前缀,而非配置顺序:
```nginx
location /api/ { ... } # 前缀长度 5
location /api/v1/ { ... } # 前缀长度 9,优先
```
请求 `/api/v1/users` 会匹配 `/api/v1/`,因为前缀更长。
**陷阱二:正则覆盖普通前缀**
```nginx
location /images/ { ... }
location ~* \.(jpg|png)$ { ... }
```
请求 `/images/logo.jpg` 会命中正则 `~*`,而非前缀 `/images/`,因为正则优先级高于普通前缀。如果希望 `/images/` 下的请求不被正则抢走,必须使用 `^~`。
**陷阱三:`=` 只匹配精确路径**
```nginx
location = /api { ... }
```
它只匹配 `/api`,不匹配 `/api/`、`/api/v1`。如果需要同时匹配,应该用前缀匹配。
### 实际配置建议
1. 高频路径用精确匹配 `=`,性能最优且语义清晰
2. 静态资源目录用 `^~`,避免被正则拦截
3. 正则匹配控制数量,按命中频率从高到低排列
4. 避免在正则中使用复杂回溯表达式,防止 ReDoS 攻击
5. 通用兜底 `location /` 放在最后
```nginx
# 精确匹配首页
location = / {
proxy_pass http://frontend;
}
# 静态资源,跳过正则检查
location ^~ /static/ {
alias /var/www/static/;
expires 30d;
}
# PHP 请求转发
location ~ \.php$ {
fastcgi_pass unix:/run/php/php-fpm.sock;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# 管理后台,跳过正则检查
location ^~ /admin/ {
auth_basic "Restricted";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://admin_backend;
}
# API 代理
location /api/ {
proxy_pass http://api_backend;
}
# 兜底
location / {
try_files $uri $uri/ /index.html;
}
```
### 追问:location 嵌套怎么用?
location 支持嵌套,但只有普通前缀匹配可以嵌套在普通前缀内,正则和精确匹配不能嵌套:
```nginx
location /api/ {
proxy_pass http://backend;
location /api/internal/ {
# 更具体的路径,覆盖外层配置
deny all;
}
}
```
内层 location 会完全覆盖外层的处理逻辑,而不是继承。如果内层也需要 `proxy_pass`,必须显式重新声明。
### 追问:location 和 rewrite 的执行顺序?
Nginx 处理请求时,rewrite 阶段在 location 匹配之前执行(server 级别的 rewrite)。如果 rewrite 修改了 URI,location 会基于修改后的 URI 重新匹配。但 location 内部的 rewrite 可能触发重新匹配,需要注意避免循环重写。服务端5月28日 07:26
Nginx 如何实现缓存?缓存策略怎么配才能防击穿?## Nginx 如何实现缓存?如何配置缓存策略?
Nginx 的缓存能力是后端服务性能优化的关键手段。面试中常从"Nginx 有哪几种缓存""proxy_cache 和 fastcgi_cache 怎么选""如何防止缓存击穿"这几个角度考察,理解原理比背配置更重要。
## Nginx 缓存的三大层次
Nginx 缓存并不是单一机制,而是分布在请求链路的不同位置:
1. **浏览器缓存**:通过响应头(`Cache-Control`、`Expires`)让客户端自行缓存,Nginx 只负责下发头信息
2. **代理缓存(proxy_cache)**:Nginx 作为反向代理时,缓存后端上游的响应,适用于反向代理场景
3. **FastCGI 缓存(fastcgi_cache)**:缓存 FastCGI 协议上游(如 PHP-FPM)的响应,适用于 PHP 直连场景
面试时先说清楚这三层,再深入其中一层的配置细节,逻辑比直接贴配置更清晰。
## 代理缓存配置详解
```nginx
http {
proxy_cache_path /var/cache/nginx/proxy
levels=1:2
keys_zone=proxy_cache:10m
max_size=1g
inactive=60m
use_temp_path=off;
server {
listen 80;
server_name example.com;
location / {
proxy_cache proxy_cache;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_bypass $http_cache_control;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
```
### 核心参数解读
| 参数 | 作用 | 注意点 |
|---|---|---|
| `levels=1:2` | 缓存目录分两级,避免单目录文件过多 | 值越大层级越深,1:2 是常用配置 |
| `keys_zone=proxy_cache:10m` | 共享内存区域,存储缓存键和元数据 | 10m 约可存 8 万条键,按需调大 |
| `max_size=1g` | 缓存磁盘上限 | 超出后 Nginx 自动淘汰最久未访问的缓存 |
| `inactive=60m` | 60 分钟无访问则淘汰 | 与 `proxy_cache_valid` 无关,是另一条淘汰链 |
| `use_temp_path=off` | 临时文件写入缓存目录而非系统临时目录 | 减少跨磁盘拷贝,生产环境建议开启 |
| `proxy_cache_valid 200 302 10m` | 200/302 响应缓存 10 分钟 | 必须显式配置,否则不缓存 |
| `proxy_cache_key` | 缓存键的计算方式 | 默认含 scheme + method + host + uri,带参请求需考虑是否加入 `$args` |
## FastCGI 缓存配置
FastCGI 缓存适用于 Nginx 直连 PHP-FPM 的场景,参数与 proxy_cache 对称:
```nginx
http {
fastcgi_cache_path /var/cache/nginx/fastcgi
levels=1:2
keys_zone=fastcgi_cache:10m
max_size=1g
inactive=60m;
server {
location ~ \.php$ {
fastcgi_cache fastcgi_cache;
fastcgi_cache_valid 200 60m;
fastcgi_cache_methods GET HEAD;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
add_header X-Cache-Status $upstream_cache_status;
fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
include fastcgi_params;
}
}
}
```
### proxy_cache 与 fastcgi_cache 怎么选?
| 对比维度 | proxy_cache | fastcgi_cache |
|---|---|---|
| 适用场景 | 反向代理后端(任何 HTTP 服务) | 直连 PHP-FPM 等 FastCGI 进程 |
| 协议 | HTTP | FastCGI |
| 灵活性 | 更通用,后端不限语言 | 仅限 FastCGI 协议 |
| 生产推荐 | 微服务、多语言后端 | 纯 PHP 架构 |
两者不能同时作用于同一 location。如果用了 `proxy_pass` 就用 proxy_cache,用了 `fastcgi_pass` 就用 fastcgi_cache。
## 缓存风暴与缓存锁定
这是面试高频追问点。当缓存过期瞬间,大量并发请求同时穿透到后端,这就是**缓存风暴(Cache Stampede)**。
```nginx
# 缓存锁定:只放一个请求去后端取数据,其余等待
proxy_cache_lock on;
proxy_cache_lock_timeout 5s;
# 过期缓存兜底:后端异常时返回旧缓存
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
# 后台异步更新:命中过期缓存时异步刷新,不阻塞请求
proxy_cache_background_update on;
```
三者配合的执行逻辑:缓存过期 → `proxy_cache_lock` 放行一个请求 → 其余请求读 `proxy_cache_use_stale` 返回旧数据 → 新数据写入后所有请求命中。这就是完整的防击穿方案。
## 动态缓存控制
不是所有请求都该缓存。用 `map` 指令按条件跳过缓存:
```nginx
# 按 URI 跳过缓存
map $request_uri $skip_cache {
default 0;
~*/admin/ 1;
~*/api/ 1;
~*/user/ 1;
}
# 按后端响应头跳过缓存
map $upstream_http_cache_control $skip_cache_by_header {
~*no-cache 1;
~*private 1;
default 0;
}
# 组合条件
map $skip_cache$skip_cache_by_header $combined_skip {
default 0;
~1 1;
}
```
然后在 location 中使用:
```nginx
proxy_cache_bypass $combined_skip;
proxy_no_cache $combined_skip;
```
`proxy_cache_bypass` 和 `proxy_no_cache` 的区别:`bypass` 是跳过缓存直接请求后端但可能将响应写入缓存;`no_cache` 则完全不写缓存。生产环境通常两者配合使用,确保该跳过的请求既不读缓存也不写缓存。
## 静态文件与浏览器缓存
静态资源的缓存走另一套逻辑,不经过 proxy_cache,直接由 Nginx 返回文件并设置浏览器缓存头:
```nginx
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
```
`immutable` 告诉浏览器:资源不会变,不需要发条件请求验证。搭配文件名加 hash(如 `app.3a7b2c.js`)效果最佳,更新时换文件名即可让浏览器重新请求。
## 缓存清除方案
Nginx 开源版不支持主动清除缓存,三种替代方案:
1. **自然过期**:通过 `proxy_cache_valid` 设定 TTL,到期自动淘汰
2. **第三方模块 ngx_cache_purge**:支持按 URL 主动清除,需编译安装
3. **删除缓存文件**:根据 `proxy_cache_key` 的 MD5 值定位文件路径并删除,`rm -rf /var/cache/nginx/proxy` 可全量清除
生产环境中,最稳妥的方式是修改 `proxy_cache_key` 加入版本号参数,发布时更新版本号让旧缓存自然失效。
## 缓存命中率监控
通过 `X-Cache-Status` 响应头可观察缓存命中情况:
```nginx
add_header X-Cache-Status $upstream_cache_status;
```
状态值含义:
| 状态 | 含义 |
|---|---|
| MISS | 未命中,请求穿透到后端 |
| BYPASS | 命中跳过条件,不走缓存 |
| EXPIRED | 缓存已过期,需重新获取 |
| STALE | 后端异常,返回过期缓存 |
| UPDATING | 缓存正在更新,返回旧内容 |
| HIT | 命中缓存,直接返回 |
监控思路:统计 HIT / (HIT + MISS + EXPIRED) 的比值,低于 80% 就需要调优缓存键或 TTL。
## 缓存配置的常见踩坑
**1. 后端响应头导致缓存不生效**
后端返回 `Cache-Control: no-cache` 或 `Set-Cookie` 时,Nginx 默认不缓存。需要忽略这些头:
```nginx
proxy_ignore_headers X-Accel-Expires Expires Cache-Control Set-Cookie;
```
**2. 缓存键缺少关键参数**
默认 `proxy_cache_key` 不含 `$args`,但 API 请求 `?page=1` 和 `?page=2` 应该返回不同内容,需要加入查询参数:
```nginx
proxy_cache_key "$scheme$request_method$host$request_uri$is_args$args";
```
**3. 缓存最小请求次数导致首次不缓存**
`proxy_cache_min_uses 2` 表示请求出现 2 次才缓存,低流量接口可能永远不缓存。生产环境建议设为 1。
**4. keys_zone 过小导致缓存元数据丢失**
keys_zone 只存键和元数据,不存响应体。10m 约存 8 万条键,key 较长时需适当增大。
## 生产环境完整配置参考
```nginx
http {
proxy_cache_path /var/cache/nginx/proxy
levels=1:2
keys_zone=proxy_cache:100m
max_size=10g
inactive=60m
use_temp_path=off;
fastcgi_cache_path /var/cache/nginx/fastcgi
levels=1:2
keys_zone=fastcgi_cache:100m
max_size=10g
inactive=60m;
map $request_uri $skip_cache {
default 0;
~*/admin/ 1;
~*/api/ 1;
~*/user/ 1;
}
server {
listen 80;
server_name example.com;
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
location / {
proxy_cache proxy_cache;
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_bypass $skip_cache;
proxy_no_cache $skip_cache;
proxy_cache_lock on;
proxy_cache_lock_timeout 5s;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
add_header X-Cache-Status $upstream_cache_status;
proxy_pass http://backend;
}
location ~ \.php$ {
fastcgi_cache fastcgi_cache;
fastcgi_cache_valid 200 60m;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
add_header X-Cache-Status $upstream_cache_status;
fastcgi_pass unix:/var/run/php/php8.0-fpm.sock;
}
}
}
```
服务端5月28日 07:24
Nginx 如何实现访问控制?有哪些访问控制方法?## Nginx 如何实现访问控制?有哪些访问控制方法?
Nginx 的访问控制是后端面试高频考点,核心思路是"在反向代理层拦截非法请求,减轻后端压力"。主要方法有五种:**IP 黑白名单**、**HTTP 基本认证**、**请求方法限制**、**基于请求头的鉴权**、**地理/时间条件控制**,实战中往往组合使用。下面逐一拆解原理和配置要点。
### 一、IP 黑白名单:最基础的网络层控制
Nginx 通过 `allow` / `deny` 指令按 IP 或 CIDR 段做访问控制,规则从上到下依次匹配,命中即生效:
```nginx
location /admin {
allow 192.168.1.0/24; # 内网放行
allow 10.0.0.0/8; # VPN 段放行
deny all; # 其余全部拒绝
proxy_pass http://backend;
}
```
**注意事项:**
- 当客户端经过代理时,`$remote_addr` 拿到的是代理 IP 而非真实客户端 IP,需要配合 `$http_x_forwarded_for` 或 `realip` 模块获取真实地址
- 白名单优先于黑名单是安全最佳实践——默认拒绝,显式放行
### 二、HTTP 基本认证:用户名密码验证
使用 `auth_basic` + `auth_basic_user_file` 实现,密码文件通过 `htpasswd` 工具生成:
```nginx
location /admin {
auth_basic "Admin Area";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://backend;
}
```
生成密码文件:
```bash
htpasswd -c /etc/nginx/.htpasswd admin_user
```
**关键点:** Basic Auth 的凭据是 Base64 编码而非加密,生产环境务必搭配 HTTPS 使用,否则密码可被中间人截获。
### 三、请求方法限制:只允许特定 HTTP 方法
用 `limit_except` 指令比 `if ($request_method)` 更规范,它在 location 级别做方法白名单:
```nginx
location /api {
limit_except GET POST {
deny all; # 只允许 GET 和 POST,其他方法返回 403
}
proxy_pass http://api_backend;
}
```
**与 `if` 写法的区别:** `limit_except` 是 Nginx 官方推荐的方式,不会触发 "if is evil" 问题,且与 `satisfy` 指令配合更好。
### 四、基于请求头的鉴权:API Key、Referer、User-Agent
**API Key 校验:**
```nginx
map $http_x_api_key $api_valid {
default 0;
"sk_prod_abc123" 1;
"sk_prod_def456" 1;
}
location /api {
if ($api_valid = 0) {
return 401;
}
proxy_pass http://api_backend;
}
```
用 `map` 比 `if` 直接比较更灵活,支持多 key 映射且可集中管理。
**防盗链(Referer 校验):**
```nginx
location /images/ {
valid_referers none blocked server_names *.example.com;
if ($invalid_referer) {
return 403;
}
root /var/www/images;
}
```
**UA 过滤:** 屏蔽恶意爬虫
```nginx
if ($http_user_agent ~* (bot|crawler|spider|scraper)) {
return 403;
}
```
### 五、地理与时间条件控制
**地理位置限制(基于 geo 模块):**
```nginx
geo $allowed_country {
default no;
CN yes;
US yes;
}
server {
location / {
if ($allowed_country = no) {
return 403;
}
proxy_pass http://backend;
}
}
```
如需精确到城市级,可用 `geoip` 模块配合 MaxMind 数据库。
**时间条件限制:**
```nginx
map $time_iso8601 $business_hours {
default 0;
~^(\d{4}-\d{2}-\d{2}T(09|1[0-9]|2[0-1])) 1;
}
location /admin {
if ($business_hours = 0) {
return 403;
}
proxy_pass http://backend;
}
```
适合管理后台只在工作时段开放的场景。
### 六、组合策略:satisfy 指令与多层防护
`satisfy any` 表示满足任一条件即可访问,`satisfy all` 表示必须全部满足:
```nginx
location /admin {
satisfy any; # IP 白名单 或 密码认证,满足其一即可
allow 192.168.1.0/24;
deny all;
auth_basic "Admin Area";
auth_basic_user_file /etc/nginx/.htpasswd;
proxy_pass http://backend;
}
```
**实战建议:** 管理后台常用 `satisfy any`——内网 IP 免密码,外网需要认证;API 接口常用 `satisfy all`——IP + Key 双重验证。
### 七、安全加固:敏感文件与目录防护
```nginx
# 禁止访问隐藏文件(如 .git、.env)
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
# 禁止访问敏感后缀文件
location ~* \.(htaccess|htpasswd|ini|log|sh|sql|bak|swp)$ {
deny all;
access_log off;
}
# 禁止目录遍历
autoindex off;
```
这些规则应作为 Nginx 配置的基线安全策略,防止信息泄露。
### 面试追问与核心要点
**Q:Nginx 访问控制的执行顺序是什么?**
`allow/deny` 按配置顺序从上到下匹配,先命中先生效。location 内的规则优先于 server 级别,server 级别优先于 http 级别。
**Q:`satisfy any` 和 `satisfy all` 的区别?**
`any` 是"或"逻辑——IP 白名单和认证满足其一即可;`all` 是"与"逻辑——两者都必须通过。默认是 `all`。
**Q:代理场景下 IP 限制为什么不生效?**
因为 `$remote_addr` 拿到的是上一层代理的 IP。解决方案:使用 `ngx_http_realip_module` 设置 `set_real_ip_from` 和 `real_ip_header X-Forwarded-For` 还原真实客户端 IP。
**Q:`if` 指令在 location 中有什么陷阱?**
Nginx 的 `if` 在 location 中属于 rewrite 阶段,可能导致非预期行为("if is evil")。能用 `map`、`limit_except`、`allow/deny` 替代的就避免用 `if`。服务端5月28日 07:23
Nginx 如何实现限流?有哪些限流策略?## Nginx 如何实现限流?有哪些限流策略?
Nginx 限流的核心思路是控制单位时间内的请求量或并发连接数,防止后端服务被流量打垮。面试中这道题主要考察三个层面:你知道哪些限流模块、你理解底层算法吗、你在生产环境怎么用。
### 限流的两种基本方式
Nginx 提供两大限流模块:
- **`limit_req`**:限制请求速率,控制单位时间内允许的请求数
- **`limit_conn`**:限制并发连接数,控制同一时刻的 TCP 连接数
两者的区别在于粒度——`limit_req` 关注的是请求频率(每秒多少个请求),`limit_conn` 关注的是连接数(同时存在多少个连接)。一个长连接可以承载多个请求,所以实际防护中通常两者配合使用。
### limit_req:请求速率限制
```nginx
http {
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s;
server {
location /api/ {
limit_req zone=api_limit burst=20 nodelay;
limit_req_status 429;
proxy_pass http://backend;
}
}
}
```
关键参数解读:
- `$binary_remote_addr`:以客户端 IP 作为限流键,二进制格式比字符串格式节省内存,10MB 共享内存大约能记录 16 万个 IP
- `zone=api_limit:10m`:定义共享内存区域名称和大小
- `rate=10r/s`:每秒允许 10 个请求,也可以用 `r/m` 表示每分钟
- `burst=20`:允许 20 个突发请求排队等待
- `nodelay`:突发请求不延迟处理,超出 burst 容量则直接拒绝
- `limit_req_status 429`:被限流时返回 429 而非默认的 503
### burst 和 nodelay 到底怎么配合?
这是面试的高频追问,很多人配置过但说不清楚原理。
**只用 `limit_req zone=api_limit`**:严格按 rate 执行,超出的请求直接 503,体验差。
**加 `burst=20`**:允许 20 个请求排队,Nginx 按 rate 速率逐个处理队列中的请求,多余请求延迟响应。好处是不误杀,坏处是用户感知延迟。
**再加 `nodelay`**:队列中的请求立即处理,不延迟响应,但队列满了还是拒绝。实际效果是「短时间内允许突发,超出就拒绝」,适合大多数 API 场景。
简单记:**burst 控制能容忍多少突发,nodelay 决定突发请求是延迟还是立即处理**。
### limit_conn:并发连接数限制
```nginx
http {
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
server {
limit_conn conn_limit 10;
proxy_pass http://backend;
}
}
```
这里 `limit_conn conn_limit 10` 表示同一个 IP 最多同时保持 10 个连接。注意这和 `limit_req` 不同——`limit_req` 限制的是请求的到达速率,`limit_conn` 限制的是连接的并发数量。
典型场景:防止单个客户端通过大量并发连接耗尽服务器资源(如慢速攻击 Slowloris)。
### 底层算法:漏桶与令牌桶
面试中问限流,必然会追问算法原理。
**漏桶算法(Leaky Bucket)**
请求像水一样倒入桶中,桶以固定速率漏水。如果桶满了,新请求被丢弃。特点是输出速率恒定,不管输入多猛烈,处理速度始终平稳。`limit_req` 不加 burst 参数时就是典型的漏桶行为——严格按 rate 处理,超出直接拒绝。
**令牌桶算法(Token Bucket)**
系统以固定速率往桶里放令牌,每个请求需要取走一个令牌。桶满了令牌不再增加。与漏桶的区别在于:令牌桶允许突发——桶里攒够了令牌时,可以一次性处理一批请求。`limit_req` 加上 burst 参数就实现了类似令牌桶的效果,允许一定程度的流量突发。
核心区别:漏桶强制匀速输出,令牌桶允许有限突发。Nginx 的 `limit_req` 实际上是两者的结合——基础速率是漏桶,burst 提供了令牌桶式的突发能力。
### 带宽限制
除了请求和连接层面的限流,Nginx 还能限制响应传输速率:
```nginx
location /download/ {
limit_rate 1m;
limit_rate_after 10m;
root /var/www/files;
}
```
- `limit_rate 1m`:限速 1MB/s
- `limit_rate_after 10m`:前 10MB 不限速,之后才限速
适用于大文件下载场景,防止少数大流量用户占满带宽。
### 白名单与动态限流
生产环境中通常需要对内部 IP 或特定请求方法豁免限流。
**基于 geo 的白名单:**
```nginx
geo $limit_key {
default $binary_remote_addr;
192.168.1.0/24 "";
10.0.0.0/8 "";
}
limit_req_zone $limit_key zone=whitelist:10m rate=10r/s;
```
白名单内的 IP 对应空字符串,不参与限流计算。
**基于请求方法的动态限流:**
```nginx
map $request_method $limit_key {
default $binary_remote_addr;
GET "";
HEAD "";
}
```
GET 和 HEAD 请求不限流,其他方法(POST、PUT 等)参与限流,适合写操作需要更严格控制的场景。
### 多层限流实战配置
```nginx
http {
# 全局限流
limit_req_zone $binary_remote_addr zone=global:10m rate=50r/s;
# API 接口限流
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
# 登录接口限流
limit_req_zone $binary_remote_addr zone=login:10m rate=5r/m;
# 连接数限制
limit_conn_zone $binary_remote_addr zone=conn:10m;
limit_req_status 429;
limit_conn_status 429;
server {
limit_conn conn 20;
location / {
limit_req zone=global burst=50 nodelay;
proxy_pass http://backend;
}
location /api/ {
limit_req zone=global burst=50 nodelay;
limit_req zone=api burst=10 nodelay;
proxy_pass http://api_backend;
}
location /login {
limit_req zone=login burst=2 nodelay;
proxy_pass http://auth_backend;
}
}
}
```
这套配置的思路是分层防护:全局兜底防 DDoS,API 层控制接口频率,登录接口单独严控防暴力破解。同一个 location 可以叠加多个 `limit_req`,任一规则触发都会拒绝请求。
### 限流日志与监控
```nginx
log_format limit '$remote_addr - [$time_local] "$request" '
'$status limit=$limit_req_status';
limit_req_log_level warn;
```
`$limit_req_status` 变量记录限流状态,`limit_req_log_level` 控制限流日志级别。生产环境建议用 warn 级别,避免日志量过大。配合 ELK 或 Prometheus 可以做限流趋势分析和告警。
### 生产环境的几个经验
1. **阈值不是拍脑袋定的**——先压测后端服务的极限 QPS,限流值设在其 70%-80% 作为安全水位
2. **429 响应要友好**——返回 JSON 格式的错误提示,带上 Retry-After 头告诉客户端多久后重试
3. **zone 内存别省**——10MB 约存 16 万 IP,如果用户量大要相应调大,内存耗尽后新请求直接 503
4. **burst 要结合业务**——API 类场景 burst 可以小一些(5-10),页面访问场景可以大一些(20-50)
5. **限流不是万能的**——在 Nginx 层限流只能防住从外到内的流量冲击,内部服务间的调用保护需要 Sentinel 或熔断器
6. **关注误杀**——公司出口 IP 共享场景下,单 IP 限流会误伤同一 NAT 后的多个用户,可考虑基于 token 或租户维度的限流键
### 追问:limit_req 和 limit_conn 该选哪个?
都要用。`limit_req` 防高频请求冲击,`limit_conn` 防连接数耗尽,两者解决不同问题。面试中如果只答一个,会被认为理解不全面。
### 追问:Nginx 限流有什么局限?
- 单机维度的限流,分布式环境下需要 Redis + Lua 或专门的限流服务
- 限流键有限,复杂业务逻辑(如按用户等级限流)需要结合 OpenResty 或网关层
- `limit_req` 基于共享内存,重启后状态丢失
- 不支持滑动窗口计数,只有固定时间窗口的速率计算服务端5月27日 22:13
Nginx 如何进行安全配置?有哪些安全最佳实践?## Nginx 如何进行安全配置?有哪些安全最佳实践?
核心思路是:隐藏信息、限制访问、加密传输、防御攻击,四层递进。
### 一、隐藏服务器指纹
攻击者第一步是信息收集,版本号是最直接的突破口。
```nginx
server_tokens off;
```
关掉之后,响应头和错误页不再暴露 Nginx 版本。面试追问:**还能怎么隐藏?** 可以用 `more_set_headers` 模块改掉 `Server` 字段本身,或者在前端反代层抹掉这个头。
### 二、访问控制与限流
IP 限制和速率限制是防暴力攻击的第一道门。
```nginx
# IP 白名单
location /admin {
allow 192.168.1.0/24;
deny all;
}
# 限流
limit_req_zone $binary_remote_addr zone=req:10m rate=10r/s;
limit_req zone=req burst=20 nodelay;
limit_conn_zone $binary_remote_addr zone=conn:10m;
limit_conn conn 10;
```
面试追问:**burst 和 nodelay 区别是什么?** burst 允许突发排队,nodelay 让排队的请求立即处理而不延时等待,否则超出的请求会被延迟处理。
### 三、SSL/TLS 与安全头
HTTPS 是底线配置,安全头加固浏览器端防护。
```nginx
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Content-Security-Policy "default-src 'self'" always;
```
面试追问:**为什么关 ssl_session_tickets?** 默认的 session ticket key 是明文保存在 worker 共享内存中的,多台 Nginx 之间无法复用,且存在前向安全性问题。集群部署时应手动轮换 key 或直接关闭。
### 四、文件与目录防护
禁止访问隐藏文件和敏感后缀,堵住信息泄露的口子。
```nginx
location ~ /\. { deny all; }
location ~* \.(htaccess|ini|log|sql|bak|swp)$ { deny all; }
autoindex off;
```
### 五、超时与缓冲区
防止慢速攻击和缓冲区溢出。
```nginx
client_body_timeout 10;
client_header_timeout 10;
keepalive_timeout 5;
send_timeout 10;
client_max_body_size 10m;
client_header_buffer_size 1k;
```
面试追问:**如果只改一个配置,改哪个?** 先关 `server_tokens`,零成本高风险。然后上 HTTPS,这是生产环境硬性要求。限流和访问控制按业务场景逐步加。
### 常见误区
用 `if` 正则匹配 SQL 注入或 XSS 关键词来拦截攻击,这是错误的。Nginx 的 `if` 在 location 中行为不稳定,且正则容易被编码绕过。防注入应交给 WAF(如 ModSecurity)或应用层参数校验,Nginx 只做流量层防护。服务端5月27日 22:13
Nginx 性能调优需要关注哪些关键参数?## Nginx 性能调优需要关注哪些关键参数?
Nginx 调优的核心思路是:减少不必要的系统调用、充分利用内核零拷贝能力、压缩传输体积、避免连接浪费。下面按影响程度从大到小逐项说明。
## Worker 进程与连接
`worker_processes` 设为 `auto`,让 Nginx 自动匹配 CPU 核心数。`worker_rlimit_nofile` 调到 100000,避免文件描述符耗尽。每个 worker 的 `worker_connections` 可设 65535,理论最大并发 = worker 数 × 65535。`multi_accept on` 让 worker 一次性接收所有就绪连接,`accept_mutex off` 在高并发下减少锁争用。
```nginx
worker_processes auto;
worker_rlimit_nofile 100000;
events {
worker_connections 65535;
use epoll;
multi_accept on;
accept_mutex off;
}
```
## 零拷贝与传输优化
`sendfile on` 跳过用户态拷贝,数据从内核直接到 socket。`tcp_nopush on` 让数据攒满一个包再发,配合 sendfile 减少系统调用次数。`tcp_nodelay on` 禁用 Nagle 算法,避免小包延迟。三者同时开启并不矛盾:tcp_nopush 在 sendfile 阶段生效,最后一个包由 tcp_nodelay 立即发出。
```nginx
sendfile on;
tcp_nopush on;
tcp_nodelay on;
```
## Gzip 压缩
压缩能将文本响应体积缩减 60%-80%,直接降低带宽和传输耗时。`gzip_comp_level` 建议设 4-6,再高收益递减但 CPU 开销上升。`gzip_min_length` 设 1024,避免压缩小响应反而变大。别忘了通过 `gzip_types` 覆盖 JSON、SVG 等常见 MIME 类型。
```nginx
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 5;
gzip_types text/plain text/css application/json application/javascript application/xml image/svg+xml;
```
## 连接复用与超时
`keepalive_timeout` 控制客户端长连接保持时间,默认 75s,可视业务缩短到 30-60s。`keepalive_requests` 限制单连接最大请求数,防止连接泄漏。对上游服务器也要开长连接:upstream 块中 `keepalive 32` 保持 32 条空闲连接,减少反复握手开销。
```nginx
keepalive_timeout 60s;
keepalive_requests 1000;
upstream backend {
server 10.0.0.1:8080;
keepalive 32;
}
```
## 缓冲区与文件缓存
`client_body_buffer_size` 和 `client_header_buffer_size` 按业务调整,过小会触发临时文件写入拖慢请求。`open_file_cache` 缓存频繁访问的文件描述符,减少磁盘 stat 调用,对静态资源场景效果显著。
```nginx
client_header_buffer_size 2k;
client_body_buffer_size 128k;
open_file_cache max=10000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
```
## 日志降耗
写日志是高并发下的隐性瓶颈。静态资源 `access_log off` 直接关掉;动态请求的日志加 `buffer=32k flush=5s`,写操作先进内存再批量落盘。
## 追问:调了参数怎么验证效果?
用 wrk 或 ab 做基准测试,对比调优前后的 QPS、P99 延迟和错误率。生产环境通过 `stub_status` 模块持续监控活跃连接数和请求处理量。记住一个原则:每次只改一个参数,观察效果后再改下一个,否则无法定位哪个改动真正有效。服务端5月27日 22:13
Nginx 日志怎么配置?有哪些格式和优化方法?## Nginx 如何配置日志?有哪些日志格式和优化方法?
Nginx 提供了访问日志(access_log)和错误日志(error_log)两种日志类型。面试中常考的是日志格式自定义、条件记录和性能优化三条主线。
## 访问日志与自定义格式
访问日志通过 `log_format` 定义格式,再用 `access_log` 引用。常用三种格式:
- **main 格式**:记录 IP、请求行、状态码、Referer、UA、耗时等基础信息
- **detailed 格式**:在 main 基础上增加 upstream 地址、状态、X-Forwarded-For、request_id
- **JSON 格式**:用 `escape=json` 转义,方便 ELK/Grafana Loki 等工具直接解析
```nginx
log_format main "$remote_addr - $remote_user [$time_local] "
""$request" $status $body_bytes_sent "
""$http_referer" "$http_user_agent" "
"$request_time $upstream_response_time";
log_format json_combined escape=json "{"
""time_local":"$time_local", ""remote_addr":"$remote_addr", ""request":"$request", ""status":"$status", ""request_time":"$request_time""
"}";
access_log /var/log/nginx/access.log main;
```
**追问:access_log 和 error_log 有什么区别?** access_log 记录每次请求的详细信息,可自定义格式;error_log 记录服务器错误,不支持自定义格式,只能设级别(debug/info/notice/warn/error/crit/alert/emerg)。
## 日志性能优化
高并发场景下日志写入会成为瓶颈,核心优化手段:
- **关闭不必要的日志**:静态资源、健康检查路径用 `access_log off`
- **缓冲写入**:`access_log ... buffer=32k flush=5s`,减少磁盘 I/O 次数
- **gzip 压缩**:`access_log ... gzip=9`,节省磁盘空间
- **条件记录**:用 `map` + `if=` 只记录特定状态码或慢请求
```nginx
map $status $loggable {
~^[23] 0;
default 1;
}
access_log /var/log/nginx/access.log main if=$loggable;
location ~* \.(css|js|jpg|png|gif|ico)$ {
access_log off;
}
access_log /var/log/nginx/access.log main buffer=32k flush=5s;
```
**追问:buffer 和 flush 参数分别控制什么?** buffer 控制缓冲区大小,写满才刷盘;flush 控制最大等待时间,超时强制刷盘。两者配合保证日志既不丢又不多写。
## 日志轮转与分离
单文件日志会无限增长,必须配合 logrotate 轮转:
```bash
# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
rotate 14
compress
delaycompress
missingok
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
}
```
`kill -USR1` 让 Nginx 重新打开日志文件,不会中断服务。
按业务分离日志同样重要:API 请求写独立文件,不同 server 块各写各的,便于精准排查问题。
## 常用变量速查
| 变量 | 说明 |
|------|------|
| $remote_addr | 客户端 IP |
| $status | 响应状态码 |
| $request_time | 请求总耗时 |
| $upstream_response_time | 上游响应时间 |
| $http_x_forwarded_for | 真实客户端 IP |
生产环境建议用 JSON 格式 + 缓冲写入 + logrotate 轮转 + 条件过滤,四板斧组合基本够用。服务端5月27日 22:12
Nginx 监控运维怎么做?stub_status、Prometheus、ELK 怎么选?## 答案前置:Nginx 监控运维的核心思路
Nginx 监控围绕三条线展开:**指标采集**(stub_status / 日志)→ **存储与展示**(Prometheus+Grafana / ELK / Zabbix)→ **告警与响应**(阈值告警 + 自动化脚本)。面试中常考的不是你背了多少工具名,而是能不能说清楚每一步为什么这么做、不同规模场景怎么选型。
## 内置指标:stub_status 能拿到什么?
stub_status 是 Nginx 自带的状态模块,开启后在指定路径暴露一组关键指标:
```nginx
location /nginx_status {
stub_status on;
access_log off;
allow 127.0.0.1;
deny all;
}
```
访问后返回:Active connections(当前活跃连接数)、accepts/handled/requests(累计连接与请求数)、Reading/Writing/Waiting(读请求头、写响应、空闲等待的连接数)。
**追问:Waiting 数量大说明什么?** 如果 Active connections ≈ Waiting,说明大量连接是 keep-alive 空闲态,连接复用率高是好事;但如果同时 Writing 偏低而请求排队,就要检查上游响应是否过慢。
## 日志监控:比 stub_status 更细的粒度
stub_status 只有粗粒度指标,真正定位问题靠日志。关键是自定义 log_format 加入上游响应时间:
```nginx
log_format detailed '$remote_addr - [$time_local] "$request" $status '
'rt=$request_time uct=$upstream_connect_time '
'uht=$upstream_header_time urt=$upstream_response_time';
access_log /var/log/nginx/detailed.log detailed;
```
`request_time` 是总耗时,`upstream_response_time` 是上游处理耗时,两者差值就是 Nginx 自身开销。如果差值大,排查 Nginx 层面的缓冲、压缩或 DNS 解析。
对于 ELK 场景,直接输出 JSON 格式日志省掉 Logstash 的 grok 解析:
```nginx
log_format json_log escape=json '{"time":"$time_local","ip":"$remote_addr",'
'"status":$status,"rt":$request_time,"urt":"$upstream_response_time"}';
access_log /var/log/nginx/json.log json_log;
```
## Prometheus + Grafana:云原生场景首选
nginx-prometheus-exporter 把 stub_status 的指标转为 Prometheus 格式,Grafana 做可视化:
```yaml
# prometheus.yml
scrape_configs:
- job_name: 'nginx'
static_configs:
- targets: ['localhost:9113']
```
**选型逻辑:** 容器化/K8s 环境下 Prometheus 是事实标准,开箱即用 Service Discovery,Grafana 社区模板丰富。Nginx Plus 用户还可以用官方 nginx-plus-exporter 拿到更细的 upstream 指标。
## ELK Stack:日志深度分析场景
当需求不只是看指标曲线,而是要按 IP、URL、状态码做聚合分析和历史追溯,ELK 更合适。Filebeat 采集 → Logstash 清洗 → Elasticsearch 存储 → Kibana 可视化,链路长但灵活度高。
**选型对比:** Prometheus 适合指标型监控(数字曲线),ELK 适合日志型分析(文本检索+聚合)。小团队二选一推荐 Prometheus,监控告警闭环更短。
## Zabbix:传统企业环境的选择
Zabbix 通过 Agent 调用 stub_status 页面,用正则提取指标配置监控项。适合已有 Zabbix 基建的企业,不推荐新项目为 Nginx 单独搭 Zabbix。
## 告警:监控闭环的关键
有监控没告警等于没监控。核心告警规则:
- 5xx 比率超阈值(如 > 1%)触发 critical
- Active connections 突增超过 2 倍标准差触发 warning
- upstream_response_time P99 > 2s 触发 warning
Prometheus 用 Alertmanager 配路由和静默,ELK 用 Watcher 或 ElastAlert,Zabbix 自带触发器机制。
## 运维常用命令速查
```bash
nginx -t # 测试配置语法
nginx -s reload # 平滑重载,不中断连接
nginx -s quit # 优雅停止,处理完当前请求后退出
nginx -s reopen # 重新打开日志文件(配合 logrotate)
```
日志轮转用 logrotate,配置 `postrotate` 里发 USR1 信号让 Nginx 重新打开文件句柄,避免写入已轮转的老文件。
## 面试追问方向
1. **stub_status 的 Waiting 和 keep-alive 是什么关系?** Waiting 连接就是 keep-alive 空闲连接,`keepalive_timeout` 控制超时回收。
2. **Nginx 502 怎么排查?** 先查 upstream 是否存活,再看 Nginx error log 里 `connect() failed` 的具体原因,最后检查 `proxy_read_timeout` 配置。
3. **如何不重启更新配置?** `nginx -t && nginx -s reload`,reload 会 fork 新 worker 加载新配置,老 worker 处理完手头请求后退出。
4. **Prometheus 和 ELK 怎么选?** 指标监控选 Prometheus(轻量、告警闭环好),日志分析选 ELK(全文检索、聚合灵活),大团队通常两套都搭。服务端5月27日 22:12
Nginx 如何配置 WebSocket 代理?## Nginx 如何配置 WebSocket 代理?
WebSocket 建立在 HTTP/1.1 之上,通过 `Upgrade` 机制将 HTTP 连接升级为全双工长连接。Nginx 默认会清除 `Upgrade` 头,所以必须手动配置才能正确代理 WebSocket。
### 核心配置(三行必写)
```nginx
location /ws {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
```
- `proxy_http_version 1.1`:WebSocket 要求 HTTP/1.1,Nginx 默认用 1.0
- `Upgrade $http_upgrade`:转发客户端的协议升级请求
- `Connection "upgrade"`:告知 Nginx 这不是普通 HTTP,需要保持升级状态
### 推荐用 map 管理 Connection 头
多 location 场景下,硬编码 `"upgrade"` 会导致非 WebSocket 请求也被标记。用 `map` 按需切换更安全:
```nginx
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
server {
location /ws {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
```
没有 `Upgrade` 头时走 `close`,普通请求不受影响。
### 超时:为什么连着连着就断了?
Nginx 的 `proxy_read_timeout` 默认 60 秒。WebSocket 是长连接,如果 60 秒内没有数据传输,Nginx 会主动断开。解决办法:
```nginx
proxy_read_timeout 3600s; # 1小时
proxy_send_timeout 3600s;
proxy_connect_timeout 60s; # 建连超时保持短即可
```
按业务调,不是越大越好。过长超时意味着僵死连接不会被回收。
### WSS(WebSocket over TLS)
在 SSL server 块里照加那三行即可,Nginx 负责 TLS 卸载,后端仍用 `ws://`:
```nginx
server {
listen 443 ssl;
ssl_certificate /etc/nginx/ssl/cert.pem;
ssl_certificate_key /etc/nginx/ssl/key.pem;
location /ws {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
```
### 负载均衡要注意会话保持
WebSocket 是有状态长连接,轮询策略会导致重连时切到不同后端。必须用 `ip_hash`:
```nginx
upstream ws_backend {
ip_hash;
server 10.0.0.1:8080;
server 10.0.0.2:8080;
}
```
如果后端是无状态的(如用 Redis Pub/Sub 做消息同步),也可以用轮询。
### 追问:连接断开怎么排查?
1. 查 Nginx error log,确认是否超时断开
2. 检查 `Upgrade` / `Connection` 头是否正确转发
3. 确认 `proxy_buffering off` 已设置,避免数据被缓冲
4. 检查防火墙或 CDN 是否拦截长连接
5. 用 `curl -H "Upgrade: websocket"` 手动测试握手是否返回 101服务端5月27日 22:11
Nginx 反向代理怎么配置?## Nginx 反向代理怎么配置?
反向代理是 Nginx 最核心的用途之一:客户端请求先到 Nginx,再由 Nginx 转发给后端服务器,客户端感知不到真实后端的存在。和正向代理(代理客户端出国)相反,反向代理代理的是服务端。
### 最小可用配置
```nginx
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://192.168.1.100:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
```
`proxy_pass` 指定后端地址,三条 `proxy_set_header` 是标配——不带这些头,后端拿到的全是 Nginx 的 IP,日志和鉴权都会出问题。
### 多后端负载均衡
```nginx
upstream backend {
server 192.168.1.100:8080 weight=3;
server 192.168.1.101:8080 weight=1;
server 192.168.1.102:8080 backup;
}
server {
listen 80;
location / {
proxy_pass http://backend;
}
}
```
四种策略选哪个?轮询(默认)无状态通用;`ip_hash` 保会话粘性但分布不均;`least_conn` 适合长连接;加权轮询按机器性能分配。生产环境常用加权轮询 + 健康检查。
### WebSocket 代理
```nginx
location /ws/ {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 3600s;
}
```
不加 `Upgrade` 头,WebSocket 握手直接失败;`proxy_read_timeout` 不加长,空闲连接会被 Nginx 主动断开,这是最常见的踩坑点。
### 面试追问:反向代理和正向代理的区别?
反向代理代理服务端,客户端不知道真实后端是谁;正向代理代理客户端,服务端不知道真实客户端是谁。一句话:正向代理帮客户端藏身份,反向代理帮服务端藏身份。
### 生产环境别漏这些
- 超时三件套:`proxy_connect_timeout`、`proxy_send_timeout`、`proxy_read_timeout`,默认 60s,慢接口要调大
- 缓冲默认开着,大文件上传场景注意 `proxy_buffer_size` 和 `proxy_buffers` 调大
- HTTPS 场景在 Nginx 做 SSL 终止,后端走内网 HTTP,证书只管 Nginx 一层
- `proxy_redirect off` 防止后端 302 重定向把内网地址暴露给客户端服务端5月27日 22:10
Nginx 如何配置 HTTPS 和 SSL 证书?## 答案
Nginx 启用 HTTPS 的核心是在 server 块中监听 443 端口并指定证书与私钥路径:
```nginx
server {
listen 443 ssl http2;
server_name example.com;
ssl_certificate /etc/nginx/ssl/example.com.crt;
ssl_certificate_key /etc/nginx/ssl/example.com.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
}
```
同时配置 HTTP 自动跳转 HTTPS:
```nginx
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
```
## 追问一:SSL 证书有哪些类型?怎么选?
- **自签名证书**:测试用,浏览器不信任
- **Let's Encrypt**:免费 DV 证书,90 天有效期,Certbot 自动申请与续期
- **商业证书**(OV/EV):CA 机构签发,验证组织身份,适合生产环境
Let's Encrypt 申请示例:
```bash
sudo certbot --nginx -d example.com -d www.example.com
```
## 追问二:如何提升 HTTPS 安全性?
四个关键措施:
1. **仅启用 TLS 1.2+**,禁用 SSLv3 和 TLS 1.0
2. **HSTS 头**,防止降级攻击:`add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;`
3. **OCSP Stapling**,减少证书验证延迟:`ssl_stapling on; ssl_stapling_verify on;`
4. **强密钥**,至少 2048 位 RSA 或 256 位 ECC
## 追问三:证书链不完整怎么办?
浏览器验证证书需要完整的信任链。若缺少中间证书,需将服务器证书与中间证书合并:
```bash
cat example.com.crt intermediate.crt > bundle.crt
```
Nginx 中指向合并后的文件:`ssl_certificate /etc/nginx/ssl/bundle.crt;`
## 追问四:多域名如何共用证书?
- **通配符证书**(`*.example.com`)覆盖子域
- **SAN 证书**支持多个域名,Certbot 申请时加多个 `-d` 参数
- **SNI**(Server Name Indication)让同一 IP 托管多张证书,Nginx 原生支持
配置验证用 `nginx -t`,无停机重载用 `nginx -s reload`。服务端5月27日 21:52
Nginx 的事件驱动模型是什么?如何实现高并发?## 答案
Nginx 采用**事件驱动 + 非阻塞 I/O** 模型,核心是 Master-Worker 进程架构 + epoll 事件循环。每个 Worker 进程单线程运行一个事件循环,通过 epoll 同时监听数千个连接的读写事件,事件就绪时回调处理,I/O 等待期间不阻塞进程,从而用少量进程支撑数万并发连接。
## 事件驱动核心机制
**epoll 的工作方式**:内核维护一个就绪队列,只有活跃连接才会触发事件通知,时间复杂度 O(1)。与传统 select/poll 的 O(n) 轮询不同,epoll 不受 FD 数量影响——这正是 Nginx 解决 C10K 问题的根基。
**事件循环流程**:
```
Worker 进程启动 → 注册监听 FD 到 epoll →
epoll_wait 阻塞等待事件 → 事件就绪返回 →
回调处理(accept/read/write) → 继续 epoll_wait
```
**连接状态机**:每个连接在 Worker 内部以状态机方式管理,经历 `等待读 → 处理请求 → 等待写 → 发送响应 → 等待新请求(keepalive)` 的状态转换,I/O 等待时让出 CPU 给其他连接处理。
## Master-Worker 进程模型
```nginx
worker_processes auto; # 通常等于 CPU 核心数
worker_rlimit_nofile 65535; # 文件描述符上限
events {
worker_connections 10240; # 单 Worker 最大连接数
use epoll; # Linux 选择 epoll
multi_accept on; # 一次 accept 多个连接
accept_mutex off; # 高并发下关闭,减少锁争用
}
```
- **Master**:管理 Worker 生命周期,加载配置,不处理业务请求
- **Worker**:各自独立运行事件循环,互不干扰,进程隔离保证稳定性
- **理论并发**:`worker_processes × worker_connections`,4 Worker × 10240 = 40960 并发
## 与 Apache 的本质区别
| 对比维度 | Nginx | Apache (prefork) |
|---------|-------|------------------|
| 模型 | 事件驱动,非阻塞 | 每连接一个进程,阻塞 |
| 内存 | 10 连接 vs 10 连点约 2MB | 10 连接约 200MB |
| C10K | 原生支持 | 受限于进程数 |
| 上下文切换 | 极少 | 频繁 |
## 高并发调优关键参数
1. **worker_processes**:设为 `auto` 或 CPU 核心数
2. **worker_connections**:根据内存调整,通常 10240-65535
3. **accept_mutex**:高并发下关闭(`off`),低并发开启防惊群
4. **系统级**:`fs.file-max`、`net.core.somaxconn`、`tcp_tw_reuse`
## 追问
- **epoll 的 LT 和 ET 模式有什么区别?Nginx 默认用哪个?** — LT 水平触发会重复通知,ET 边沿触发只通知一次,Nginx 默认 LT,配合非阻塞 I/O 确保数据读完
- **为什么 Worker 单线程还能处理上万连接?** — 因为 99% 时间连接在等 I/O,事件驱动只在 I/O 就绪时才占用 CPU
- **accept_mutex 是什么?什么时候该关?** — 惊群控制锁,防止所有 Worker 同时争抢新连接。连接数远大于 Worker 数时关闭可提升吞吐服务端5月27日 21:45
Nginx 的负载均衡有哪些策略?如何配置?## Nginx 负载均衡有哪些策略?如何配置?
Nginx 通过 `upstream` 模块实现负载均衡,内置 5 种策略,另有第三方扩展策略。面试核心答案:轮询(默认)、加权轮询、最少连接、IP 哈希、一致性哈希。
### 1. 轮询(Round Robin,默认)
按顺序依次分配请求,服务器性能相近时使用。
```nginx
upstream backend {
server 192.168.1.100:8080;
server 192.168.1.101:8080;
}
```
### 2. 加权轮询(Weighted Round Robin)
权重越高分配越多,适用于服务器性能不均。
```nginx
upstream backend {
server 192.168.1.100:8080 weight=3;
server 192.168.1.101:8080 weight=1;
}
```
### 3. 最少连接(Least Connections)
将请求分给当前活动连接数最少的服务器,适用于请求处理时间差异大的场景。
```nginx
upstream backend {
least_conn;
server 192.168.1.100:8080;
server 192.168.1.101:8080;
}
```
### 4. IP 哈希(IP Hash)
同一客户端 IP 始终分配到同一台服务器,实现会话保持。
```nginx
upstream backend {
ip_hash;
server 192.168.1.100:8080;
server 192.168.1.101:8080;
}
```
**注意**:ip_hash 在后端服务器增减时会导致大量请求重新分配,一致性哈希可解决此问题。
### 5. 一致性哈希(Hash)
基于指定 key(如 URI、Cookie)做哈希,`consistent` 参数启用一致性哈希算法,减少服务器变动时的映射抖动。
```nginx
upstream backend {
hash $request_uri consistent;
server 192.168.1.100:8080;
server 192.168.1.101:8080;
}
```
### 服务器状态参数
```nginx
upstream backend {
server 192.168.1.100:8080 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.101:8080 weight=2 max_fails=3 fail_timeout=30s;
server 192.168.1.102:8080 down; # 永久下线
server 192.168.1.103:8080 backup; # 备用,主服务不可用时启用
server 192.168.1.104:8080 max_conns=100; # 最大并发连接数
}
```
### 策略选择速查
| 场景 | 推荐策略 |
|---|---|
| 服务器性能相近 | 轮询 |
| 服务器性能不均 | 加权轮询 |
| 请求处理时间差异大 | 最少连接 |
| 需要会话保持 | IP 哈希 / 一致性哈希 |
| 需要缓存命中 | 一致性哈希(基于 URI) |
### 完整配置示例
```nginx
http {
upstream backend {
least_conn;
server 192.168.1.100:8080 weight=3 max_fails=3 fail_timeout=30s;
server 192.168.1.101:8080 weight=2 max_fails=3 fail_timeout=30s;
server 192.168.1.103:8080 backup;
keepalive 32;
}
server {
listen 80;
server_name example.com;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
```
### 健康检查
开源版 Nginx 仅支持被动健康检查(通过 `max_fails` / `fail_timeout` 判断),商业版 Nginx Plus 提供主动健康检查。
### 面试追问
**Q:ip_hash 和一致性哈希的区别?**
ip_hash 以客户端 IP 为 key,后端变动时映射大规模失效;一致性哈希通过虚拟环减少变动影响,且可自定义 key(URI、Cookie 等),灵活性更高。
**Q:Nginx 负载均衡能做四层转发吗?**
能。使用 `stream` 模块可做 TCP/UDP 四层负载均衡,配置方式与 HTTP 的 `upstream` 类似。
**Q:如何实现更精细的会话保持?**
Nginx Plus 支持 `sticky cookie` 指令;开源版可配合一致性哈希 `hash $cookie_jsessionid consistent` 实现基于 Cookie 的会话粘滞。前端2024年7月15日 23:58
如何动态修改 nginx 的配置信息?### 动态配置 Nginx 的方法
确实,动态配置 Nginx 是在不重启服务的情况下更改配置的实用能力。这对于需要高可用性的生产环境尤其重要。以下是几种可以实现动态配置Nginx的方法:
#### 1. 使用 `nginx -s reload`
这是最常见的动态修改Nginx配置的方法。修改完nginx的配置文件后,可以使用 `nginx -s reload` 命令来重新加载配置文件,这样做可以不中断服务。这个命令实际上会启动新的worker进程,并逐渐停止旧的worker进程。例如:
```bash
sudo nginx -s reload
```
#### 2. 使用 Consul 和 Consul Template
Consul 是一个服务网络解决方案,可以用来动态地处理服务发现和配置。搭配使用 Consul Template 可以动态生成Nginx配置文件。Consul Template 监控Consul的状态变化,一旦检测到变化,就会重新渲染配置模板并重新加载Nginx。这种方法适用于基于服务发现的动态配置场景。
#### 3. 使用 OpenResty
OpenResty 是一个基于Nginx与Lua的动态web平台,它允许通过编写Lua脚本来动态地更改配置逻辑。例如,可以在access阶段根据请求的不同动态改变代理服务器或者其他配置。这种方法提供了极高的灵活性。
#### 4. 使用 Docker 容器
在Docker容器中运行Nginx时,可以通过更新Docker容器的配置来实现Nginx的动态配置。这通常涉及到使用环境变量或挂载配置卷来修改配置。容器化管理工具(如Kubernetes)可以在不停机的情况下滚动更新Nginx配置。
#### 5. 动态模块
Nginx也支持动态模块,这些模块可以在不重新编译Nginx的情况下加载或卸载。这使得用户可以根据需要添加或删除功能,虽然这不直接修改Nginx的配置文件,但它提供了一种方式来扩展Nginx的功能而不需要重启服务。
### 结论
动态配置Nginx主要目的是减少因配置更改而导致的服务中断。上述方法各有优势,适用于不同的场景和需求。在选择具体的实现方式时,应评估实际的业务需求、资源和技术栈。