标签

Nginx

Nginx 是一个网络和代理服务器。Nginx (发音为 "engine-x") 是一个高性能的 HTTP 和反向代理服务器,同时也是一个 IMAP/POP3 代理服务器。Nginx 是由 Igor Sysoev 开发的,最初发布于2004年,旨在解决 C10k 问题,即同时处理大量客户端连接的需求。由于其高性能、稳定性、丰富的功能集以及低资源消耗,Nginx 在全球范围内广泛用于提供网页内容,特别是在高流量的网站中非常流行。

Nginx
查看更多相关内容
服务端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 的会话粘滞。
服务端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. **版本控制**:使用文件名哈希实现缓存更新