Linux第19篇:Nginx 反向代理与负载均衡——Java SaaS 的流量入口

📅 2026/7/10 23:32:47
Linux第19篇:Nginx 反向代理与负载均衡——Java SaaS 的流量入口
本文导读本文是系列第 19 篇。Nginx 是 Java SaaS 架构中几乎必不可少的流量入口负责 SSL 卸载、反向代理、负载均衡、静态文件服务。本文讲透 Nginx 核心配置结构、反向代理 Spring Boot 的完整配置、五种负载均衡策略对比、HTTPS 自动证书Lets Encrypt以及大文件上传、超时等高频生产踩坑。关键词Nginx反向代理教程、Nginx负载均衡配置、Nginx HTTPS配置、Lets Encrypt证书、Nginx反向代理Spring Boot、Nginx生产配置目录一、Nginx 在 Java SaaS 架构中的角色二、Nginx 安装与目录结构三、反向代理 Spring Boot 的完整配置四、五种负载均衡策略的选型指南五、Lets Encrypt 自动 HTTPS 证书配置六、高频生产踩坑大文件上传与超时调优6.1 大文件上传配置6.2 upstream 后端宕机时的 fail_timeout 调优七、常见问题解答FAQ❓ Q1Nginx reload 和 restart 分别什么时候用❓ Q2用了 Nginx 反向代理后Spring Boot 里获取到的客户端 IP 一直是 127.0.0.1怎么解决❓ Q3Nginx 配置了多个 server 块请求到底会匹配到哪个❓ Q4如何配置 Nginx 对 AI 推理接口的流式响应SSE/Server-Sent Events❓ Q5Nginx 的 worker_processes 和 worker_connections 怎么配置本篇小结与系列导航 参考资料一、Nginx 在 Java SaaS 架构中的角色直接把 Spring Boot 的 8080 端口对外暴露不是生产环境应有的做法——原因是多方面的Spring Boot 内嵌的 Tomcat 不擅长处理 SSL 卸载性能开销相对 Nginx 更高、静态文件服务没有 Nginx 的 sendfile 优化、连接数管理处理大量并发长连接时资源效率低于事件驱动的 Nginx。Nginx 作为前置流量入口把这些通用能力从业务代码里解耦出来是成熟 Java SaaS 架构的标准配置。Nginx 在 Java SaaS 架构中的位置 互联网用户 │ ▼ HTTPS443 ┌───────────────────────────────────────┐ │ Nginx流量入口 │ │ ● SSL/TLS 卸载HTTPS → HTTP │ │ ● 静态文件服务不经过 Spring Boot │ │ ● 反向代理转发动态请求 │ │ ● 负载均衡多实例分流 │ │ ● 限流/防盗链/访问控制 │ └────────────┬──────────────────────────┘ │ HTTP8080 ▼ ┌────────────────────────────┐ │ Spring Boot 应用集群 │ │ 实例1:8080 实例2:8081 │ └────────────────────────────┘二、Nginx 安装与目录结构# Ubuntu/Debian 通过 apt 安装推荐大多数场景 sudo apt install -y nginx # 查看 Nginx 版本和编译参数 nginx -V # 启动 Nginx 并设置开机自启 sudo systemctl enable --now nginx # Nginx 的关键目录结构 ls /etc/nginx/ # nginx.conf → 主配置文件全局配置 # conf.d/ → 按站点拆分的独立配置文件推荐在这里创建每个站点的配置 # sites-available/ → Ubuntu 默认的站点配置目录和 conf.d 二选一即可 # sites-enabled/ → 已启用的站点通常是 sites-available 的符号链接理解nginx.conf的层级结构是正确配置 Nginx 的前提nginx.conf 配置层级 main 块全局配置 ├── events 块网络模型配置 └── http 块HTTP 服务配置 ├── upstream 块定义后端服务器组 └── server 块定义虚拟主机 └── location 块定义 URL 路由规则三、反向代理 Spring Boot 的完整配置下面是一份经过生产验证、覆盖常见需求的完整 Nginx 配置# 文件路径/etc/nginx/conf.d/myapp.conf # 定义后端服务器组upstream upstream myapp_backend { server 127.0.0.1:8080; # Spring Boot 应用监听的地址和端口 keepalive 32; # 保持 32 个长连接复用减少 TCP 建连开销 } server { listen 80; server_name api.myapp.com; # 你的域名可以写多个空格分隔 # 把 HTTP 请求重定向到 HTTPS生产环境必须配置 return 301 https://$host$request_uri; } server { listen 443 ssl; listen [::]:443 ssl; http2 on; # 开启 HTTP/2需要 Nginx 1.25旧版用 listen 443 ssl http2; server_name api.myapp.com; # SSL 证书配置路径在第五节申请 Lets Encrypt 证书后会有 ssl_certificate /etc/letsencrypt/live/api.myapp.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/api.myapp.com/privkey.pem; # SSL 安全配置禁用老旧协议只保留 TLS 1.2/1.3 ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 1d; # 客户端请求相关配置 client_max_body_size 100m; # 允许上传的最大文件大小默认只有 1m上传大文件必须调大 # 日志配置 access_log /var/log/nginx/myapp_access.log; error_log /var/log/nginx/myapp_error.log warn; # 反向代理配置 location / { proxy_pass http://myapp_backend; # 把客户端真实 IP 传递给后端否则 Spring Boot 看到的 IP 全是 127.0.0.1 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; # 超时配置根据业务实际情况调整 proxy_connect_timeout 10s; # 与后端建立连接的超时 proxy_send_timeout 60s; # 向后端发送数据的超时 proxy_read_timeout 60s; # 等待后端响应的超时 # 关闭代理缓冲适合流式响应、SSE 等场景普通 API 可以保持默认开启 # proxy_buffering off; } # 静态文件直接由 Nginx 服务不经过 Spring Boot大幅减轻应用负担 location /static/ { alias /opt/myapp/static/; expires 30d; # 设置浏览器缓存时间减少重复请求 add_header Cache-Control public, no-transform; } # Actuator 健康检查端点只允许内网访问 location /actuator/health { proxy_pass http://myapp_backend; allow 127.0.0.1; allow 192.168.0.0/16; deny all; # 拒绝其他所有来源 } }# 检查配置语法是否正确修改后务必先用这个验证 sudo nginx -t # 输出 syntax is ok 和 test is successful 才说明配置无误 # 重新加载配置不重启 Nginx 进程对已有连接无影响 sudo systemctl reload nginx # 重启会短暂中断当前连接一般不需要reload 就够了 sudo systemctl restart nginx⚠️ 踩坑记录每次修改 Nginx 配置后在 reload 之前必须先执行nginx -t验证语法这个步骤很多人嫌麻烦而跳过结果一个配置文件语法错误导致nginx reload失败Nginx 保持旧配置继续运行这是 reload 的容错机制不会直接崩掉。但如果是nginx restart配合语法有误的配置文件Nginx 会启动失败这时候线上流量就真的中断了。养成改配置 →nginx -t→ reload这个铁律是 Nginx 运维的安全底线。另外reload和restart的语义差异很重要reload是平滑热加载Worker 进程用新配置处理新请求老请求继续用旧配置完成无连接中断restart是重启所有进程会中断已有连接绝大多数配置变更都应该用reload而不是restart。四、五种负载均衡策略的选型指南当 Spring Boot 应用运行多个实例时Nginx 的upstream模块提供了多种负载均衡算法# 策略一轮询Round Robin默认策略 # 请求依次分发给每个后端适合后端处理能力相当的场景 upstream myapp_backend { server 192.168.1.101:8080; server 192.168.1.102:8080; server 192.168.1.103:8080; } # 策略二加权轮询Weighted Round Robin # 处理能力强的服务器分配更多请求weight 数值越大分到的比例越高 upstream myapp_backend { server 192.168.1.101:8080 weight3; # 分到 3/5 的请求 server 192.168.1.102:8080 weight2; # 分到 2/5 的请求 } # 策略三IP Hash # 同一个客户端 IP 的请求始终路由到同一台后端会话保持适合有状态的应用 upstream myapp_backend { ip_hash; server 192.168.1.101:8080; server 192.168.1.102:8080; } # 策略四最少连接数Least Connections需要 Nginx Plus 或第三方模块 # 把请求发给当前连接数最少的后端适合请求处理时间差异大的场景 upstream myapp_backend { least_conn; server 192.168.1.101:8080; server 192.168.1.102:8080; } # 策略五权重 健康检查配置生产推荐 upstream myapp_backend { server 192.168.1.101:8080 weight1 max_fails3 fail_timeout30s; server 192.168.1.102:8080 weight1 max_fails3 fail_timeout30s; # max_fails在 fail_timeout 时间窗口内允许的最大失败次数 # fail_timeout超过 max_fails 后这台服务器被暂时标记为不可用的时间 keepalive 32; } 生产提示选择负载均衡策略的关键判断点是你的应用是有状态还是无状态。对于无状态的 Spring Boot 应用Session 存在 Redis 里、Token 认证等轮询或加权轮询就是最好的选择实现最简单、负载最均匀对于有状态的应用Session 存在内存里不同实例间 Session 无法共享才需要考虑 ip_hash 来保证会话粘性。实践中把应用改造成无状态Session 外置到 Redis然后用简单轮询比用 ip_hash 维护有状态应用的架构简洁得多——ip_hash 在某台后端故障下线时会导致部分用户 Session 丢失运维上也是一个额外的复杂度不如从根本上解决有状态的问题。五、Lets Encrypt 自动 HTTPS 证书配置Lets Encrypt 提供免费的 SSL/TLS 证书Certbot是其官方客户端能自动申请证书并配置好 Nginx# 第一步安装 Certbot 和 Nginx 插件 sudo apt install -y certbot python3-certbot-nginx # 第二步申请证书Certbot 会自动验证域名归属并修改 Nginx 配置 # 前提域名已经解析到当前服务器的公网 IP且 80 端口可以从外部访问 sudo certbot --nginx -d api.myapp.com -d www.myapp.com # 过程中会询问邮箱用于接收证书过期提醒和是否强制 HTTPS选 2 强制重定向 # 第三步验证证书申请成功 sudo certbot certificates # 会显示已申请的证书信息包括有效期Lets Encrypt 证书有效期为 90 天 # 第四步测试自动续期证书到期前会自动续期这里验证自动续期功能正常 sudo certbot renew --dry-runCertbot 安装后会自动创建一个 systemd Timer 或 crontab 任务定期通常每天两次检查证书是否快过期并在过期前 30 天左右自动续期。这意味着你申请一次证书后几乎不需要再手动干预证书会永远保持有效——这正是 Lets Encrypt 相比购买商业证书在运维便利性上的核心优势。⚠️ 踩坑记录使用 Lets Encrypt 证书有两个常见失败场景①域名没有正确解析到服务器IPCertbot 申请证书时会用 HTTP-01 验证方式在服务器上临时创建一个文件Lets Encrypt 服务器来访问这个文件确认你控制了这个域名如果域名解析不对Lets Encrypt 访问的是别的服务器验证必然失败②80 端口被防火墙拦截验证过程需要外部能访问你服务器的 80 端口如果防火墙或云安全组没有开放 80 端口验证同样失败。申请前先用curl -v http://api.myapp.com/从外部不是服务器本机测试 80 端口是否可达能省去很多排查时间。六、高频生产踩坑大文件上传与超时调优6.1 大文件上传配置# Nginx 默认只允许最大 1MB 的请求体client_max_body_size 默认为 1m # 如果上传大文件Nginx 会在接收完请求前就返回 413 (Request Entity Too Large) # 解决在 server 或 location 块里调大这个限制 server { # 允许最大 500MB 的上传根据实际业务需求设置不要无限制放大 client_max_body_size 500m; # 同时调整 Spring Boot 的 multipart 限制两者都要改缺一不可 # application-prod.yml: # spring.servlet.multipart.max-file-size: 500MB # spring.servlet.multipart.max-request-size: 500MB }⚠️ 踩坑记录大文件上传的 413 错误是一个双重限制问题——Nginx 这边有client_max_body_sizeSpring Boot 这边有spring.servlet.multipart.max-file-size两个都必须修改只改其中一个不会解决问题。很多人只改了 Nginx 这边发现还是报错或者只改了 Spring Boot 这边Nginx 还是在应用层之前就拒绝了请求。另外Nginx 的超时参数proxy_read_timeout对大文件上传也至关重要——上传 500MB 的文件在带宽受限时可能需要几分钟如果proxy_read_timeout设的是默认的 60 秒上传可能在 60 秒后被 Nginx 强制断开导致大文件上传必然失败。对于文件上传的 location 块建议单独设置更长的超时proxy_read_timeout 600s;。6.2 upstream 后端宕机时的 fail_timeout 调优upstream myapp_backend { server 192.168.1.101:8080 max_fails3 fail_timeout30s; server 192.168.1.102:8080 max_fails3 fail_timeout30s; }当一台后端服务器连续失败 3 次max_failsNginx 会把它标记为不可用在 30 秒内不再给它分发请求fail_timeout。30 秒后 Nginx 会尝试再次把请求发给这台服务器如果成功则重新纳入服务。这个机制的作用是当某台后端意外宕机时Nginx 能快速感知并把流量自动切走不需要人工干预这是 Nginx 负载均衡的核心容错能力。但要注意fail_timeout设得太短比如 5 秒会导致频繁在可用和不可用之间反复切换服务器刚标记不可用就恢复了又开始接流量又失败又标记不可用产生不必要的抖动太长比如 5 分钟会导致后端恢复后很久 Nginx 都不给它分配流量浪费了恢复的实例30 秒是一个经过实践验证的合理默认值。七、常见问题解答FAQ❓ Q1Nginxreload和restart分别什么时候用几乎所有配置变更用reload就够了。reload是优雅重载Worker 进程用新配置处理新请求老连接继续完成后退出对业务无感知。只有在升级 Nginx 二进制本身安装新版本时才需要restart且应该走更优雅的流程nginx -s upgrade。直接restart会中断所有已有连接在流量较大的生产环境是一个有影响的操作非必要不执行。❓ Q2用了 Nginx 反向代理后Spring Boot 里获取到的客户端 IP 一直是 127.0.0.1怎么解决配置文件里需要加proxy_set_header X-Real-IP $remote_addr本篇已包含Spring Boot 侧需要配置信任代理头在application.yml里加server.forward-headers-strategy: frameworkSpring Boot 2.2这样 Spring Boot 的HttpServletRequest.getRemoteAddr()就会返回从X-Real-IP头里取到的真实客户端 IP而不是代理服务器127.0.0.1的地址。❓ Q3Nginx 配置了多个 server 块请求到底会匹配到哪个匹配逻辑按以下优先级精确匹配server_name如server_name api.myapp.com最优先以~/~*开头的正则匹配次之通配符前缀*.myapp.com再次通配符后缀myapp.*最后如果都没匹配到请求会落到默认服务器有default_server标记的 server 块如果没有显式标记则是配置文件里第一个 server 块。合理规划server_name是避免请求落到意外 server 块的关键。❓ Q4如何配置 Nginx 对 AI 推理接口的流式响应SSE/Server-Sent EventsSSE 是长连接响应数据会分批推送默认的 Nginx 代理缓冲机制会把数据攒到一定量才往客户端发导致客户端感知到明显延迟。对 SSE 的 location 需要关闭代理缓冲location /api/chat/stream { proxy_pass http://myapp_backend; proxy_buffering off; # 关闭代理缓冲数据实时透传 proxy_cache off; proxy_read_timeout 300s; # SSE 长连接读超时设长一些 proxy_set_header Connection ; proxy_http_version 1.1; # SSE 需要 HTTP/1.1 的 keep-alive chunked_transfer_encoding on; }❓ Q5Nginx 的worker_processes和worker_connections怎么配置worker_processes推荐设为autoNginx 自动检测 CPU 核心数并创建对应数量的 Worker 进程worker_connections是每个 Worker 进程能同时处理的最大连接数结合worker_processes得出服务器能处理的总并发连接数。worker_connections受系统文件描述符限制ulimit -n通常设为1024到65536区间需要同步调整操作系统的fs.file-max内核参数第28篇会详细讲让 OS 层面的限制不成为瓶颈。本篇小结与系列导航 核心结论Nginx 是 Java SaaS 生产架构中处理 SSL 卸载、反向代理、负载均衡的标准流量入口。配置修改后必须用nginx -t验证语法再reload绝不应该在未验证的情况下直接restart。负载均衡策略选型的核心判断点是应用是否有状态无状态应用用轮询、有状态应用改造到无状态比维护 ip_hash 更合理。client_max_body_size和 Spring Boot 的 multipart 限制需要同时配置只改一处无效。Lets Encrypt Certbot 提供免费且自动续期的 HTTPS 证书是中小 SaaS 服务 HTTPS 化的首选方案。AI 推理服务的流式响应SSE需要在对应 location 块关闭proxy_buffering。 参考资料Nginx 官方文档Nginx 反向代理模块文档Lets Encrypt 官方文档Certbot 官方安装指南如果本文对你有帮助请点赞 收藏 ⭐ 支持一下欢迎在评论区留言交流。系列标签LinuxNginx反向代理负载均衡HTTPSLets EncryptSpring BootJava运维