服务端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 只做流量层防护。标签
Nginx
Nginx 是一个网络和代理服务器。Nginx (发音为 "engine-x") 是一个高性能的 HTTP 和反向代理服务器,同时也是一个 IMAP/POP3 代理服务器。Nginx 是由 Igor Sysoev 开发的,最初发布于2004年,旨在解决 C10k 问题,即同时处理大量客户端连接的需求。由于其高性能、稳定性、丰富的功能集以及低资源消耗,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 的会话粘滞。服务端2月21日 16:58
Nginx 如何优化静态资源?有哪些优化策略?## Nginx 如何优化静态资源?有哪些优化策略?
Nginx 在提供静态资源方面表现优异,通过合理的配置可以显著提升静态资源的加载速度和用户体验。
### 启用高效文件传输:
```nginx
http {
# 启用 sendfile
sendfile on;
# 启用 tcp_nopush
tcp_nopush on;
# 启用 tcp_nodelay
tcp_nodelay on;
server {
listen 80;
server_name example.com;
root /var/www/html;
location / {
try_files $uri $uri/ =404;
}
}
}
```
### Gzip 压缩:
```nginx
http {
# 启用 Gzip
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css text/xml text/javascript
application/json application/javascript
application/xml+rss application/rss+xml
application/atom+xml image/svg+xml;
gzip_disable "msie6";
# 静态资源预压缩
gzip_static on;
server {
listen 80;
server_name example.com;
root /var/www/html;
location / {
try_files $uri $uri/ =404;
}
}
}
```
### 浏览器缓存:
```nginx
server {
listen 80;
server_name example.com;
root /var/www/html;
# 静态资源长期缓存
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# HTML 文件短期缓存
location ~* \.html$ {
expires 1h;
add_header Cache-Control "public, must-revalidate";
}
# 不缓存动态内容
location ~* \.php$ {
expires off;
add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0";
}
location / {
try_files $uri $uri/ =404;
}
}
```
### 文件缓存:
```nginx
http {
# 打开文件缓存
open_file_cache max=100000 inactive=20s;
open_file_cache_valid 30s;
open_file_cache_min_uses 2;
open_file_cache_errors on;
server {
listen 80;
server_name example.com;
root /var/www/html;
location / {
try_files $uri $uri/ =404;
}
}
}
```
### 静态资源分离:
```nginx
server {
listen 80;
server_name example.com;
# 主站点
location / {
root /var/www/html;
try_files $uri $uri/ =404;
}
# 静态资源
location /static/ {
root /var/www/static;
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# 图片资源
location /images/ {
root /var/www/images;
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
# 字体文件
location /fonts/ {
root /var/www/fonts;
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
add_header Access-Control-Allow-Origin *;
}
}
```
### CDN 集成:
```nginx
server {
listen 80;
server_name example.com;
root /var/www/html;
# 重写静态资源 URL 到 CDN
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2)$ {
return 301 https://cdn.example.com$request_uri;
}
location / {
try_files $uri $uri/ =404;
}
}
```
### 图片优化:
```nginx
server {
listen 80;
server_name example.com;
root /var/www/html;
# 图片缓存
location ~* \.(jpg|jpeg|png|gif|ico|svg|webp)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
# WebP 支持
if ($http_accept ~* "webp") {
rewrite ^(.+)\.(jpg|png)$ $1.webp last;
}
}
# 图片防盗链
location ~* \.(jpg|jpeg|png|gif)$ {
valid_referers none blocked example.com *.example.com;
if ($invalid_referer) {
return 403;
}
}
location / {
try_files $uri $uri/ =404;
}
}
```
### 字体文件优化:
```nginx
server {
listen 80;
server_name example.com;
root /var/www/html;
# 字体文件
location ~* \.(woff|woff2|ttf|otf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
# CORS 支持
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, OPTIONS";
add_header Access-Control-Allow-Headers "Origin, Content-Type";
}
location / {
try_files $uri $uri/ =404;
}
}
```
### 静态资源预加载:
```nginx
server {
listen 80;
server_name example.com;
root /var/www/html;
location = / {
add_header Link "</style.css>; rel=preload; as=style, </script.js>; rel=preload; as=script, </image.jpg>; rel=preload; as=image";
try_files $uri $uri/ =404;
}
location / {
try_files $uri $uri/ =404;
}
}
```
### HTTP/2 推送:
```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;
root /var/www/html;
# HTTP/2 推送
location = / {
http2_push /style.css;
http2_push /script.js;
http2_push /image.jpg;
try_files $uri $uri/ =404;
}
location / {
try_files $uri $uri/ =404;
}
}
```
### 静态资源合并:
```nginx
# 使用第三方模块 ngx_http_concat_module
server {
listen 80;
server_name example.com;
root /var/www/html;
# CSS 合并
location /static/css/ {
concat on;
concat_types text/css;
concat_unique on;
concat_max_files 10;
}
# JS 合并
location /static/js/ {
concat on;
concat_types application/javascript;
concat_unique on;
concat_max_files 10;
}
location / {
try_files $uri $uri/ =404;
}
}
```
### 完整静态资源优化配置:
```nginx
user nginx;
worker_processes auto;
worker_rlimit_nofile 65535;
events {
worker_connections 10240;
use epoll;
multi_accept on;
}
http {
# 基础优化
sendfile on;
tcp_nopush on;
tcp_nodelay on;
# Gzip 压缩
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css text/xml text/javascript
application/json application/javascript
application/xml+rss application/rss+xml
application/atom+xml image/svg+xml;
gzip_disable "msie6";
gzip_static on;
# 文件缓存
open_file_cache max=100000 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;
index index.html;
# 静态资源优化
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires $expires;
add_header Cache-Control "public, immutable";
access_log off;
}
# 字体文件 CORS
location ~* \.(woff|woff2|ttf|otf|eot)$ {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods "GET, OPTIONS";
add_header Access-Control-Allow-Headers "Origin, Content-Type";
}
# 图片防盗链
location ~* \.(jpg|jpeg|png|gif)$ {
valid_referers none blocked example.com *.example.com;
if ($invalid_referer) {
return 403;
}
}
# 主路由
location / {
try_files $uri $uri/ =404;
}
# 禁止访问隐藏文件
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
}
}
```
### 静态资源优化最佳实践:
1. **启用压缩**:使用 Gzip 压缩文本资源
2. **合理缓存**:根据资源类型设置缓存时间
3. **文件分离**:将静态资源分离到独立域名或 CDN
4. **预压缩**:使用 gzip_static 预压缩静态文件
5. **HTTP/2**:启用 HTTP/2 提升加载速度
6. **图片优化**:使用 WebP 格式,启用图片压缩
7. **字体优化**:使用 WOFF2 格式,启用 CORS
8. **监控性能**:使用 Lighthouse 等工具监控性能
9. **定期清理**:清理未使用的静态资源
10. **版本控制**:使用文件名哈希实现缓存更新