Spring Boot生产部署必须用Nginx反向代理的5大核心原因 1. 为什么用Nginx部署Spring Boot Jar包而不是直接java -jar你刚写完一个Spring Boot项目本地测试跑得飞快打包成jar后双击也能启动但一上线就卡壳——用户访问时要么打不开要么超时要么连不上。这时候有老同事拍着你肩膀说“别直接java -jar了上Nginx。”你心里嘀咕我这又不是Web服务器又没HTML页面加个Nginx不是多此一举其实这不是“加一层”而是补上生产环境里最基础、最关键的三块拼图反向代理、静态资源卸载、请求入口统一管控。Nginx在这里根本不是替代Spring Boot而是做它做不到、也不该做的事。Spring Boot内嵌的Tomcat或Jetty本质是应用容器专注业务逻辑执行而Nginx是专业的边缘网关天生擅长处理海量并发连接、SSL终止、路径重写、负载分发和故障隔离。举个生活化的例子Spring Boot就像一家餐厅的主厨负责把食材变成菜Nginx则是餐厅门口的迎宾前台保安点单系统——它不炒菜但它决定谁进门、走哪条通道、菜单怎么展示、高峰期怎么分流、有人闹事怎么隔离还能顺手把饮料和纸巾静态资源提前备好不让厨师反复跑腿。实际项目中我见过太多团队踩坑某公司把Spring Boot直接暴露在公网用8080端口对外服务结果被扫描器盯上三天内遭遇27次目录遍历试探另一家没配Nginx前端Vue项目和后端jar全挤在一个端口跨域问题改了八版配置还是403还有团队用systemd直接托管jar结果日志没分离、OOM崩溃后不自动拉起、HTTPS证书更新要重启整个应用……这些问题Nginx一条proxy_pass指令几行location规则就能根治。更关键的是Nginx让部署具备了“可演进性”。今天你只有一个jar明天可能拆成用户服务、订单服务、支付服务三个jar后天要接入API网关或灰度发布——只要Nginx配置层保持稳定后端服务可以自由增减、替换技术栈甚至混用Java/Go/Python完全不影响用户访问体验。这种解耦能力是直接裸跑jar永远无法提供的底层弹性。所以这不是“要不要加Nginx”的问题而是“什么时候必须加Nginx”的问题。答案很明确只要这个Spring Boot服务要面向真实用户、要长期在线、要应对不确定流量就必须加Nginx——它不是锦上添花而是生产环境的准入门槛。2. 整体架构设计与方案选型逻辑2.1 为什么选反向代理模式而不是正向代理或直接监听先厘清一个常见误解很多人以为Nginx部署Spring Boot就是“让Nginx去调用jar”这没错但关键在于“怎么调”。我们采用的是反向代理Reverse Proxy而非正向代理Forward Proxy。两者的本质区别在于正向代理代表客户端去访问目标服务器比如浏览器配置代理IP而反向代理代表服务器接收所有外部请求再按规则分发给后端真实服务。对用户而言他只和Nginx打交道完全不知道背后是Java还是PHP是单机还是集群。为什么必须是反向代理因为Spring Boot jar默认绑定localhost:8080这是典型的“仅本机可访问”策略。如果直接把jar绑定到0.0.0.0:8080并开放防火墙端口等于把应用容器直接暴露在互联网上——没有请求过滤、没有连接数限制、没有SSL卸载、没有路径隔离。而Nginx作为反向代理天然形成一道屏障它监听80/443端口接收用户请求再以内部网络如127.0.0.1:8080安全地转发给Spring Boot彻底隔绝外部对应用端口的直接访问。提示绝对不要在生产环境将Spring Boot的server.address设为0.0.0.0并开放该端口。这是高危操作等同于拆除服务器防火墙的最后一道门。2.2 为什么不选Apache HTTPD或CaddyApache历史悠久模块丰富但对现代微服务场景略显笨重。它的进程模型prefork/workers在高并发短连接场景下内存开销大配置语法冗长且对WebSocket支持需额外编译模块。Caddy确实优雅自动HTTPS、配置简洁但其插件生态和企业级运维工具链如日志分析、监控集成、配置热加载稳定性目前仍不如Nginx成熟。某次我们为某高校教务系统做压测Caddy在持续10万并发连接下出现偶发502错误排查发现是其默认的HTTP/2流控阈值与Spring Boot的Keep-Alive设置冲突而Nginx经多年打磨在同等条件下稳如磐石。我们最终选择Nginx核心基于三点硬指标连接承载能力epoll事件驱动模型单机轻松支撑10万并发连接内存占用仅为Apache的1/3配置可靠性nginx -t语法检查nginx -s reload平滑重载线上零停机变更已成标配生态兼容性与Prometheus、Grafana、ELK日志体系深度集成运维脚本、Ansible角色、Docker镜像均高度标准化。2.3 静态资源到底放哪Nginx托管 vs Spring Boot内置Spring Boot默认通过spring.resources.static-locations提供静态资源服务如/static下的JS/CSS/图片。但在生产环境这会带来两个隐形成本一是JVM堆内存被大量缓存静态文件占用GC压力增大二是每次HTTP请求都要经过Spring MVC拦截链哪怕只是下载一个1KB的favicon.ico也要走一遍DispatcherServlet——性能损耗可达30%以上。我们的方案是所有静态资源由Nginx直接响应Spring Boot只专注动态API。具体做法是在Nginx配置中用location精确匹配静态路径前缀如/assets/,/images/,/js/并指向磁盘上的物理目录location ^~ /assets/ { alias /var/www/myapp/assets/; expires 1y; add_header Cache-Control public, immutable; }这里^~表示前缀匹配优先级最高确保不会被后续的/通配规则覆盖expires 1y强制浏览器长期缓存immutable告诉浏览器该资源内容永不改变可跳过ETag验证。实测下来首页加载时间从1.2秒降至0.4秒CDN回源流量下降65%。注意Spring Boot中必须关闭静态资源处理否则会出现Nginx和Spring Boot双重响应冲突。在application.yml中添加spring: web: resources: add-mappings: false2.4 HTTPS终结为何必须在Nginx层完成Spring Boot虽支持server.ssl.*配置启用HTTPS但存在严重缺陷密钥和证书直接放在应用代码中版本管理困难TLS握手由JVM处理性能远低于Nginx的OpenSSL原生实现更致命的是一旦应用重启SSL会话缓存丢失导致用户首次访问TLS握手耗时激增。正确做法是Nginx做SSL终止SSL Termination。它监听443端口用OpenSSL解密HTTPS请求再以明文HTTP协议http://127.0.0.1:8080转发给Spring Boot。这样做的好处是TLS卸载由专业网络层完成CPU消耗降低40%会话复用Session Resumption、OCSP Stapling等高级特性开箱即用证书更新只需重载Nginx无需重启Java应用可集中管理HSTS、CSP、X-Frame-Options等安全头。我们曾对比过同一台4核8G服务器Nginx终结HTTPS时QPS达8200而Spring Boot自建HTTPS时QPS仅4900且在高并发下频繁出现javax.net.ssl.SSLException: handshake timed out。3. 核心配置详解与实操步骤3.1 Nginx基础配置结构解析Nginx配置不是一堆指令的堆砌而是一个有层级、有继承、有作用域的树状结构。理解其骨架才能避免“改了一行全站崩掉”的惨剧。核心配置文件通常位于/etc/nginx/nginx.conf其标准结构如下# 全局块影响整个Nginx进程 user nginx; worker_processes auto; worker_rlimit_nofile 65535; events { # 事件块定义连接处理模型 use epoll; worker_connections 65535; } http { # HTTP块所有HTTP服务的基础配置 include /etc/nginx/mime.types; default_type application/octet-stream; # 日志格式、超时时间、压缩等全局设置 log_format main $remote_addr - $remote_user [$time_local] $request $status $body_bytes_sent $http_referer $http_user_agent $http_x_forwarded_for; # 虚拟主机块每个server就是一个独立站点 server { listen 80; server_name example.com; location / { proxy_pass http://127.0.0.1:8080; } } }关键点在于http块内的配置如log_format、gzip对所有server生效而server块内的配置如listen、server_name只对该虚拟主机有效location块则进一步细化到URL路径级别。这种层级设计让复杂配置变得可维护。实操心得永远不要在http块里写proxy_pass它必须出现在location或server块内。我曾帮某电商团队排查一个诡异问题他们把proxy_pass写在http块导致所有server都默认转发到同一个后端结果用户访问A网站却看到B网站的内容——这就是作用域误用的典型后果。3.2 Spring Boot Jar包部署前的必要准备部署不是把jar丢到服务器就完事。一个健壮的生产级jar包需要提前做好五件事第一禁用内嵌Tomcat的HTTP端口暴露。在application.yml中明确指定绑定地址为localhost防止意外监听外网server: address: localhost port: 8080这样即使防火墙策略失效外部也无法直连8080端口。第二配置合理的线程池与连接超时。Spring Boot默认Tomcat最大线程数200对高并发不够。我们根据Nginx的proxy_read_timeout通常设为60秒反推将Tomcat的connection-timeout设为65秒避免Nginx等待超时而Tomcat还在处理server: tomcat: max-connections: 10000 accept-count: 100 max-threads: 200 min-spare-threads: 50第三启用Actuator健康检查端点。Nginx可通过/actuator/health探针判断应用是否存活。在pom.xml中加入dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-actuator/artifactId /dependency并配置application.ymlmanagement: endpoint: health: show-details: when_authorized endpoints: web: exposure: include: health,info,metrics,prometheus第四日志输出重定向到文件。避免System.out.println污染控制台使用Logback配置滚动文件!-- logback-spring.xml -- appender nameFILE classch.qos.logback.core.rolling.RollingFileAppender file/var/log/myapp/app.log/file rollingPolicy classch.qos.logback.core.rolling.TimeBasedRollingPolicy fileNamePattern/var/log/myapp/app.%d{yyyy-MM-dd}.%i.log/fileNamePattern timeBasedFileNamingAndTriggeringPolicy classch.qos.logback.core.rolling.SizeAndTimeBasedFNATP maxFileSize100MB/maxFileSize /timeBasedFileNamingAndTriggeringPolicy /rollingPolicy /appender第五制作systemd服务单元文件。让jar包像系统服务一样管理支持开机自启、日志查看、状态监控# /etc/systemd/system/myapp.service [Unit] DescriptionMy Spring Boot Application Afternetwork.target [Service] Typesimple Userappuser WorkingDirectory/opt/myapp ExecStart/usr/bin/java -Xms512m -Xmx1024m -jar /opt/myapp/myapp.jar Restartalways RestartSec10 StandardOutputjournal StandardErrorjournal [Install] WantedBymulti-user.target启用服务sudo systemctl daemon-reload sudo systemctl enable myapp sudo systemctl start myapp。3.3 Nginx核心代理配置逐行说明以下是一份经过千次生产验证的Nginx配置模板我们逐行拆解其设计意图upstream backend { server 127.0.0.1:8080 max_fails3 fail_timeout30s; # 可扩展为多实例server 192.168.1.10:8080; server 192.168.1.11:8080; } server { listen 80; server_name myapp.example.com; return 301 https://$server_name$request_uri; # 强制HTTP跳转HTTPS } server { listen 443 ssl http2; server_name myapp.example.com; # SSL证书配置路径根据实际调整 ssl_certificate /etc/letsencrypt/live/myapp.example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/myapp.example.com/privkey.pem; ssl_trusted_certificate /etc/letsencrypt/live/myapp.example.com/chain.pem; # 安全加固禁用弱协议和加密套件 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; # HSTS头强制浏览器后续只用HTTPS访问 add_header Strict-Transport-Security max-age31536000; includeSubDomains; preload always; # 防止MIME类型嗅探攻击 add_header X-Content-Type-Options nosniff; # 防止点击劫持 add_header X-Frame-Options DENY; # CSP策略根据实际资源调整 add_header Content-Security-Policy default-src self; script-src self unsafe-inline unsafe-eval; style-src self unsafe-inline; img-src self data:;; # 静态资源路径映射 location ^~ /static/ { alias /var/www/myapp/static/; expires 1y; add_header Cache-Control public, immutable; } location ^~ /uploads/ { alias /var/www/myapp/uploads/; expires 1h; } # API接口代理关键 location /api/ { 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; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; # 超时设置必须大于Spring Boot的处理时间 proxy_connect_timeout 5s; proxy_send_timeout 60s; proxy_read_timeout 60s; # 缓冲区优化防大响应体截断 proxy_buffering on; proxy_buffer_size 128k; proxy_buffers 4 256k; proxy_busy_buffers_size 256k; # WebSocket支持如项目用到 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; } # 根路径代理适配前端History路由 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; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; proxy_connect_timeout 5s; proxy_send_timeout 30s; proxy_read_timeout 30s; } # 健康检查端点供Nginx主动探测 location /actuator/health { proxy_pass http://backend/actuator/health; proxy_cache_valid 200 302 10m; proxy_cache_valid 404 1m; proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; } }重点参数解读upstream backend定义后端服务组max_fails3 fail_timeout30s表示连续3次失败后30秒内不再将请求发给该节点实现自动故障隔离。proxy_set_header系列必须透传原始请求信息否则Spring Boot的RequestHeader(X-Forwarded-For)或ServletRequest.getRemoteAddr()会拿到127.0.0.1丢失真实用户IP。proxy_buffering on开启缓冲避免Nginx边收边转导致大文件响应被截断proxy_buffer_size设为128k是经验值能容纳大部分HTTP头。proxy_http_version 1.1Upgrade头这是WebSocket代理的关键缺一不可否则前端new WebSocket(wss://...)会报400错误。注意proxy_pass末尾的/符号至关重要。proxy_pass http://backend/;表示将/api/user重写为/user转发而proxy_pass http://backend;无斜杠则原样转发为/api/user。务必根据Spring Boot的server.servlet.context-path配置匹配。3.4 Lets Encrypt免费证书自动化部署HTTPS不能靠手动拷贝证书维持。我们采用Certbot Nginx插件实现全自动续期第一步安装Certbotsudo apt update sudo apt install certbot python3-certbot-nginx -y第二步首次申请证书需域名DNS已解析到该服务器IPsudo certbot --nginx -d myapp.example.comCertbot会自动修改Nginx配置添加SSL相关指令并重载服务。第三步验证自动续期# 手动测试续期流程不实际更新证书 sudo certbot renew --dry-run # 查看续期任务Ubuntu默认已创建systemd timer sudo systemctl list-timers | grep certbotCertbot默认每天凌晨2:15和下午2:15各运行一次续期检查证书到期前30天自动更新。我们额外添加一个监控钩子在续期成功后发送通知# 编辑 /etc/letsencrypt/renewal-hooks/deploy/notify.sh #!/bin/bash if [ $RENEWED_LINEAGE /etc/letsencrypt/live/myapp.example.com ]; then echo $(date): SSL certificate renewed for myapp.example.com | mail -s SSL Renewal Alert adminexample.com fi实操心得Certbot的--nginx插件虽方便但会覆盖你手动写的Nginx配置。因此所有自定义配置如安全头、静态资源路径必须写在server块内且Certbot生成的SSL配置块应放在自定义块之后。我们习惯把自定义配置放在/etc/nginx/conf.d/myapp-custom.confCertbot生成的SSL配置在/etc/nginx/sites-enabled/myapp通过include顺序控制优先级。4. 实操全流程与关键环节验证4.1 从零开始的完整部署流水线我们以一台全新的Ubuntu 22.04服务器为例演示从环境初始化到服务上线的完整步骤。全程可复制粘贴执行每一步都有明确目的阶段一系统初始化# 创建专用用户禁止root登录 sudo adduser appuser sudo usermod -aG sudo appuser sudo sed -i s/PermitRootLogin yes/PermitRootLogin no/ /etc/ssh/sshd_config sudo systemctl restart sshd # 更新系统安装基础工具 sudo apt update sudo apt upgrade -y sudo apt install curl wget git unzip vim htop -y # 安装Java 17Spring Boot 3.x要求 sudo apt install openjdk-17-jdk -y java -version # 验证输出包含17.x.x # 安装Nginx sudo apt install nginx -y sudo systemctl enable nginx sudo systemctl start nginx curl -I http://localhost # 应返回200 OK阶段二部署Spring Boot Jar包# 创建应用目录上传jar包此处用wget模拟 sudo mkdir -p /opt/myapp sudo chown appuser:appuser /opt/myapp sudo -u appuser wget -O /opt/myapp/myapp.jar https://example.com/releases/myapp-1.0.0.jar # 创建日志目录 sudo mkdir -p /var/log/myapp sudo chown appuser:appuser /var/log/myapp # 创建systemd服务文件 sudo tee /etc/systemd/system/myapp.service /dev/null EOF [Unit] DescriptionMy Spring Boot Application Afternetwork.target [Service] Typesimple Userappuser WorkingDirectory/opt/myapp ExecStart/usr/bin/java -Xms512m -Xmx1024m -jar /opt/myapp/myapp.jar Restartalways RestartSec10 StandardOutputjournal StandardErrorjournal [Install] WantedBymulti-user.target EOF sudo systemctl daemon-reload sudo systemctl enable myapp sudo systemctl start myapp # 验证应用启动 sudo systemctl status myapp # 看到active (running) sudo journalctl -u myapp -f # 实时查看日志确认无ERROR curl -s http://localhost:8080/actuator/health | jq . # 应返回{status:UP}阶段三配置Nginx反向代理# 备份默认配置 sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak # 创建自定义配置 sudo tee /etc/nginx/conf.d/myapp.conf /dev/null EOF upstream backend { server 127.0.0.1:8080 max_fails3 fail_timeout30s; } server { listen 80; server_name myapp.example.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name myapp.example.com; ssl_certificate /etc/ssl/certs/ssl-cert-snakeoil.pem; ssl_certificate_key /etc/ssl/private/ssl-cert-snakeoil.key; # 临时用自签名证书后续替换为Lets Encrypt ssl_protocols TLSv1.2 TLSv1.3; add_header Strict-Transport-Security max-age31536000; includeSubDomains; preload always; 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; proxy_set_header X-Forwarded-Proto $scheme; proxy_connect_timeout 5s; proxy_send_timeout 30s; proxy_read_timeout 30s; } } EOF # 测试配置语法 sudo nginx -t # 必须显示successful # 重载Nginx sudo systemctl reload nginx # 验证Nginx代理是否生效 curl -I http://myapp.example.com # 应返回301跳转 curl -Ik https://myapp.example.com # 应返回200且有Strict-Transport-Security头阶段四申请正式SSL证书# 安装Certbot sudo apt install certbot python3-certbot-nginx -y # 申请证书需确保myapp.example.com DNS已指向此服务器IP sudo certbot --nginx -d myapp.example.com # Certbot会自动修改配置并重载Nginx验证 sudo nginx -t sudo systemctl reload nginx curl -Ik https://myapp.example.com # 检查证书是否为Lets Encrypt签发阶段五静态资源与安全加固# 创建静态资源目录 sudo mkdir -p /var/www/myapp/static sudo chown -R appuser:appuser /var/www/myapp # 将前端构建产物如dist目录复制进去 # sudo -u appuser cp -r /path/to/dist/* /var/www/myapp/static/ # 修改Nginx配置添加静态资源块在server块内 sudo sed -i /location \//i \ location ^~ /static/ {\ alias /var/www/myapp/static/;\ expires 1y;\ add_header Cache-Control public, immutable;\ } /etc/nginx/conf.d/myapp.conf # 添加安全头在server块内 sudo sed -i /add_header Strict-Transport-Security/a \ add_header X-Content-Type-Options nosniff;\ add_header X-Frame-Options DENY;\ add_header Content-Security-Policy default-src \self\; script-src \self\ \unsafe-inline\ \unsafe-eval\; style-src \self\ \unsafe-inline\; img-src \self\ data:;; /etc/nginx/conf.d/myapp.conf sudo nginx -t sudo systemctl reload nginx4.2 关键环节验证清单与预期结果部署完成后必须逐项验证不能只看“页面打开了”。以下是生产环境必须通过的12项验证点附带验证命令和预期输出验证项验证命令预期结果说明1. Nginx监听状态sudo ss -tlnp | grep :80|:443显示nginx: master process监听*:80和*:443确认Nginx进程正常绑定端口2. Spring Boot进程状态sudo systemctl status myapp | grep ActiveActive: active (running)确保应用服务已启动3. 后端健康检查curl -s http://localhost:8080/actuator/health | jq .statusUP验证Spring Boot自身健康4. Nginx代理连通性curl -s http://localhost/actuator/health | jq .statusUP验证Nginx能成功代理到后端5. HTTP强制跳转curl -I http://myapp.example.com | head -1HTTP/1.1 301 Moved Permanently确保HTTP请求被重定向6. HTTPS响应状态curl -I https://myapp.example.com | head -1HTTP/2 200或HTTP/1.1 200 OK验证HTTPS可访问7. 证书有效性echoopenssl s_client -connect myapp.example.com:443 2/dev/null | openssl x509 -noout -datesnotAfter...日期在3个月内8. 真实IP透传curl -s https://myapp.example.com/api/test-ip | jq .remoteAddr返回你的公网IP非127.0.0.1验证X-Forwarded-For生效9. 静态资源缓存curl -I https://myapp.example.com/static/main.js | grep Cache-ControlCache-Control: public, immutable确认静态资源强缓存10. 安全头存在curl -I https://myapp.example.com | grep -E (Strict-Transport-SecurityX-Content-Type-OptionsX-Frame-Options)11. 大文件上传支持curl -X POST https://myapp.example.com/api/upload -F filelarge.zip返回200及成功JSON测试client_max_body_size是否足够12. 错误页面友好性curl -I https://myapp.example.com/nonexistentHTTP/2 404或HTTP/2 500非Nginx默认错误页确保Spring Boot接管404/500注意第8项“真实IP透传”是高频出错点。如果返回127.0.0.1请检查Spring Boot中是否配置了server.forward-headers-strategyframeworkSpring Boot 2.2或server.tomcat.remoteip.remote-ip-headerx-forwarded-for旧版并在Nginx中确认proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;已正确设置。4.3 性能压测与瓶颈定位部署完成不等于高枕无忧。我们用wrk进行基础压测定位潜在瓶颈第一步安装wrksudo apt install build-essential libssl-dev git -y git clone https://github.com/wg/wrk.git cd wrk make sudo cp wrk /usr/local/bin/第二步基准压测模拟100并发持续30秒wrk -t12 -c100 -d30s https://myapp.example.com/actuator/health预期结果QPS应在800平均延迟50ms。若QPS低于300需排查。第三步分层诊断Nginx层瓶颈sudo nginx -T \| grep worker_connections确认连接数上限sudo ss -s查看当前连接数sudo nginx -V 21 \| grep -o with-http_v2_module确认HTTP/2已启用。网络层瓶颈sudo iftop -P 443观察443端口实时流量sudo netstat -s \| grep -i retransmitted检查TCP重传率2%需关注。Spring Boot层瓶颈jstack $(pgrep -f myapp.jar)查看线程阻塞jstat -gc $(pgrep -f myapp.jar) 1s监控GC频率curl https://myapp.example.com/actuator/metrics/jvm.memory.used查看内存使用。我们曾遇到一个典型案例压测QPS卡在420jstat显示Full GC每分钟2次。排查发现application.yml中-Xmx1024m设置过小而应用缓存了大量用户会话。将-Xmx提升至2048m后QPS跃升至1100Full GC消失。实操心得永远不要凭感觉调优。先用wrk定性再用jstat/ss/iftop定量最后用jstack/jmap定位。某次我们发现Nginx的worker_connections 65535看似很大但ulimit -n系统限制只有1024导致实际并发上限被卡死——这就是“配置了等于没配”的经典陷阱。5. 常见问题与独家排查技巧实录5.1 502 Bad Gateway最痛的“黑屏”502错误意味着Nginx成功连接到后端但后端返回了无效响应。这是生产环境最高频的故障原因多样我们按发生概率排序原因1Spring Boot未启动或崩溃现象curl http://localhost:8080/actuator/health返回Failed to connect排查sudo systemctl status myapp看服务状态sudo journalctl -u myapp -n 50查最近50行日志sudo ss -tlnp \| grep :8080确认端口是否监听解决重启服务sudo systemctl restart myapp若反复崩溃检查/var/log/myapp/app.log中的OutOfMemoryError或数据库连接超时原因2Nginx与后端超时时间不匹配现象Nginx日志/var/log/nginx/error.log中出现up