结果导向 · 能力全景 / 反向代理 / 负载均衡 / HTTPS / 限流 / 灰度发布
Nginx 是什么(一句话):一个高性能的 HTTP 服务器 + 反向代理,用事件驱动(epoll)单机扛几万并发连接。几乎是所有 Web 项目的"统一入口标配"。
什么场景该用 Nginx:
| 场景 | Nginx 在里面干什么 | 不用会怎样 |
|---|---|---|
| Java/Node/Python 服务对外 | 反向代理,隐藏后端真实地址,统一 80/443 入口 | 后端直接暴露端口,证书/限流/日志都得各服务自己搞 |
| 多台后端实例分流 | 负载均衡,把请求分到多台机器 | 得自己写分流逻辑或用更重的网关 |
| 前端静态资源(JS/CSS/图片) | 直接吐文件,比 Tomcat 快一个量级 | 静态走后端应用服务器,浪费应用性能 |
| HTTPS 上线 | 证书在 Nginx 终结,后端走 HTTP,证书集中管理 | 每个后端服务都要配证书,运维噩梦 |
| 灰度/蓝绿/A-B 发布 | 按比例或按规则切流量到新版本 | 得停机切换或上更复杂的发布系统 |
定位:Nginx 不是一个应用服务器(不跑你的业务代码),它是一个"流量入口层"。你的 Spring Boot 应用是"干活的人",Nginx 是"门口的前台 + 调度员",所有外部请求先到它,它再分发、过滤、转发。
核心能力全景(6 大块):
常被拿来和 Nginx 比的有:Apache(老牌 HTTP 服务器)、HAProxy(专业负载均衡器)、云厂商 ALB/SLB(云负载均衡)。结果导向直接看下表:
| 维度 | Nginx | Apache | HAProxy | 云 ALB/SLB |
|---|---|---|---|---|
| 定位 | HTTP服务器+反向代理+LB,全能 | 老牌HTTP服务器 | 专业四层/七层负载均衡 | 云上托管的负载均衡 |
| 并发模型 | 事件驱动(epoll),单机几万连接 | 多进程/多线程,连接多了吃内存 | 事件驱动,专精LB | 云端弹性,不用管容量 |
| 强项 | 静态资源+反代+LB+HTTPS+限流全都能干 | 动态模块丰富,老项目兼容 | LB 策略最全,健康检查最细 | 免运维,自动扩容,和高可用绑定 |
| 弱项 | 健康检查较弱(开源版被动检查) | 并发能力不如 Nginx | 不能吐静态文件,不做HTTPS证书管理 | 贵(按量计费),配置灵活度低 |
| 什么时候选它 | ✅ 默认选择,Web 入口几乎都用它 | 维护老项目才用 | 需要极精细 LB 策略/健康检查时 | 全量上云、不想自己运维 LB 时 |
Nginx 的配置是嵌套块结构,从外到内四层:main → events → http → server → location。理解这个层级,看任何 Nginx 配置都不会懵。
请求匹配 location 的流程(最容易踩坑的地方):
下面是生产里用得最多的 7 段配置,每段都能直接抄。配置都用 IDE 窗口展示,重点是看懂每行在干嘛。
request.getRemoteAddr() 拿到的是 Nginx 的 IP(127.0.0.1),不是用户真实 IP。要拿真实 IP 得读 X-Forwarded-For。Spring Boot 里用 request.getHeader("X-Forwarded-For") 取。不传这三个头,后端日志、风控、限流全瞎。
| 策略 | 适用场景 | 注意 |
|---|---|---|
| 轮询 | 后端机器配置一样,无状态 | 默认,不用写 |
| 权重 | 机器配置不一样,强的多扛 | — |
| ip_hash | Session 存本地、要会话保持 | 用户 IP 变了会漂移,不完美 |
| least_conn | 请求处理时间差异大 | 最均衡,推荐 |
| 一致性hash | 有本地缓存,要尽量命中同一台 | 扩容时只影响一部分流量 |
try_files ... /index.html 是前端路由刷新 404 的唯一解。Vue/React 项目上线必查这三点。
背景:某电商首页大促前压测,QPS 上到 2000,后端 Spring Boot 扛不住——因为前端 JS/CSS/图片都从 Tomcat 走,Tomcat 线程全被静态资源占满了,业务接口排队超时。
方案:静态资源交给 Nginx 直接吐,业务接口走反代。改完 QPS 5000 都扛得住。
背景:订单服务要上线 v2 版本(重构了计费逻辑),不敢全量上。用 Nginx split_clients 做灰度,先放 10% 流量观察,逐步放量到 100%。
nginx -t 校验 → ③nginx -s reload 热加载 → ④盯监控看 v2 错误率/耗时 → ⑤没问题继续放量,有问题改回 0% 秒级回滚。整个过程不停机、不丢请求(reload 是优雅的,老 worker 处理完手头请求再退)。
背景:某活动接口 /api/coupon/grab 被脚本疯狂刷,QPS 飙到 8000,后端 MySQL 连接池被打满,正常用户全部超时。加 Nginx 限流:单 IP 每秒最多 10 次,超出直接 503。
现象:配了两个 location,想给 /api/admin 走管理后台服务,/api/ 走主服务。结果所有 /api/ 开头的请求(包括 admin)全进了主服务。
原因:两个都是普通前缀匹配,Nginx 选了最长前缀匹配,/api/admin 应该匹配 admin 那个才对……但配的人误以为"写在前面优先",又用了 ~ 正则干扰了优先级。
正解:记住优先级 = > ^~ > ~ / ~* > 普通前缀。要确保某个 location 一定优先,用 =(精确)或 ^~(前缀且不再走正则)。
现象:请求 /api/user/login,后端收到的路径变成了 //user/login 或 /api/user/login,导致 Spring Boot 路由 404。
原因:proxy_pass 末尾有没有斜杠,决定了 Nginx 转发时要不要把 location 匹配的部分去掉。这个规则坑了无数人:
正解:后端如果用 @RequestMapping("/api/user"),Nginx 就不要加斜杠(保留 /api);后端如果用 @RequestMapping("/user"),Nginx 就加斜杠(去掉 /api)。规则统一:前后端路径前缀要对齐。
现象:双 11 零点流量峰值,大量用户访问直接报 502/连接拒绝,error.log 刷屏 worker_connections are not enough。
原因:默认 worker_connections 1024,4 核机器 = 4 worker × 1024 = 4096 连接上限。反代场景下每个连接要占 2 个(客户端+后端),实际只能扛 2000 并发,大促直接打满。
正解:生产环境标配调到 worker_connections 10240,同时改系统 ulimit(ulimit -n 65535)。公式:最大连接 = worker_processes × worker_connections / 2(反代场景)。
现象:两套都出过事。第一套 burst=0 nodelay,正常用户点快一点就被 503,客服投诉爆了;第二套改成 burst=100(不加 nodelay),结果刷子照样打进来 100 个排队请求,后端还是被压垮。
原因:没理解 burst 的含义。burst 是"突发桶容量"——允许超出 rate 的请求数。burst 太小误伤正常用户,太大防不住刷子。
正解:按业务定 burst。登录类接口 burst=5(用户偶尔连点),查询类 burst=20,秒杀类 burst=0 直接拒(防刷优先)。原则:保护后端用小 burst + nodelay,保护用户体验用大 burst 不加 nodelay。
要一起用,分工不同。Tomcat 是应用服务器(跑 Spring Boot 业务代码),Nginx 是入口层(静态资源、反代、负载均衡、HTTPS)。
典型架构:用户 → Nginx (80/443) → Tomcat (8080)。Nginx 吐静态文件、做 HTTPS、做限流,Tomcat 专心跑业务。不要让 Tomcat 直接对外——它吐静态文件慢、没有限流、HTTPS 配置麻烦、单机扛并发能力弱。
worker_processes = CPU 核数(写 auto 自动匹配)。每个 worker 是单进程,多核机器开多个 worker 才能用满 CPU。
worker_connections:静态服务 1024 够用,反代服务生产标配 10240。反代场景每个请求占 2 个连接(客户端+后端),所以实际并发上限 = worker_processes × worker_connections / 2。4 核 × 10240 / 2 = 20480 并发,够大多数场景。
同时记得改系统 ulimit(ulimit -n 65535),否则系统层文件描述符限制会先卡住。
因为经过 Nginx 转发后,后端拿到的请求信息变了:
• 不加 Host:后端 request.getServerName() 拿到的是 Nginx 的 IP,不是域名。生成绝对 URL、重定向会错。
• 不加 X-Forwarded-For:后端 request.getRemoteAddr() 拿到的是 Nginx 的 IP(127.0.0.1),日志、风控、限流全拿不到用户真实 IP。
所以反代必加这三件套:proxy_set_header Host $host; + X-Real-IP $remote_addr; + X-Forwarded-For $proxy_add_x_forwarded_for;。Spring Boot 取真实 IP:request.getHeader("X-Forwarded-For")。
能做一部分,但不是专业 API 网关。Nginx 能做路由、限流、HTTPS、灰度,但不擅长:鉴权(JWT 校验得写 Lua)、协议转换、请求/响应改写、可编程逻辑。
区别:Nginx 是"静态配置的流量入口",改路由要 reload;Spring Cloud Gateway / Kong 是"可编程 API 网关",支持动态路由、过滤器链、用代码/Lua 写复杂逻辑。
实际选择:简单路由+限流+HTTPS → Nginx 够了;微服务需要动态路由+鉴权+协议转换 → 上 Spring Cloud Gateway 或 Kong。很多团队是 Nginx → API 网关 → 微服务 两层并存。
不会断请求。用 nginx -s reload,过程是:
① master 进程收到 reload 信号 → ②启动新的 worker 进程加载新配置 → ③通知老 worker"处理完手头请求就退出" → ④老 worker 处理完正在进行的请求后优雅退出 → ⑤新 worker 接管。整个过程不丢请求、不停服。
生产标配流程:nginx -t(校验语法)→ nginx -s reload(热加载)。改配置永远先 -t 再 reload,避免语法错误导致整个 Nginx 挂掉。
升级 Nginx 版本用 kill -USR2 热升级,新老进程并存、逐步切换,也是零停机。
1. 定位:流量入口层,不是应用服务器。静态它吐、动态它转、HTTPS 它结、限流它挡。
2. 配置四层:main → events → http → server → location。location 优先级 = > ^~ > ~ > 前缀,记住能避 80% 配错。
3. 生产必做:静态资源剥离、反代透传三个头、worker_connections 调到 10240、改配置先 -t 再 reload。