Java服务公网部署全流程:从本地Spring Boot到可访问网站

📅 2026/7/16 4:08:24 👁️ 阅读次数 📝 编程学习
Java服务公网部署全流程:从本地Spring Boot到可访问网站

1. 这不是“部署Java项目”,而是把你的Java服务变成别人能访问的网站

你搜“java项目 部署 到公网详细 教程”,心里真正想问的,大概率是这三件事之一:

  • 我本地写好的Spring Boot项目,怎么让女朋友用她手机浏览器打开?
  • 公司测试环境跑得好好的,一上服务器就404,连Tomcat首页都打不开,日志里全是Connection refused
  • 没有宽带运营商给的固定公网IP,家里那台老笔记本装了MySQL和Java后端,怎么让同事远程调接口?

这三个问题,本质都是同一个动作:把运行在你电脑上的Java进程,变成互联网上一个可被任意设备通过HTTP协议访问的地址(比如http://yourname.me:8080/api/users。它不等于“打包成jar扔到服务器上就完事”,更不是复制粘贴几行命令就能通关的游戏。它是一条链路:从JVM启动参数、端口监听、防火墙策略、域名解析,到反向代理配置、SSL证书、甚至运营商NAT穿透——每个环节卡住,整条链就断。

我干过五年Java后端部署支持,帮37家中小公司做过线上环境迁移,也亲手在树莓派、旧MacBook、甚至一台刷了OpenWrt的路由器上跑过Spring Boot。踩过的坑比写的代码还多:

  • Tomcat启动成功但浏览器打不开,查了两小时发现是阿里云安全组没开8080端口,而控制台默认只显示“实例状态正常”;
  • 域名解析生效了,但HTTPS报错NET::ERR_CERT_AUTHORITY_INVALID,结果发现Let's Encrypt的acme.sh脚本在CentOS 7上默认用的是Python 2.7,而certbot要求Python 3.6+;
  • 用Docker部署后API响应慢得像拨号上网,最后发现是宿主机磁盘IO被MySQL占满,而Java容器没配--memory限制,直接抢光了所有内存。

这篇教程不讲“什么是JDK”“Spring Boot是什么”,也不堆砌mvn clean package这种教科书命令。它只聚焦一件事:让你的Java服务,在真实网络环境下,稳定、安全、可维护地暴露给公网用户。你会看到:

  • 为什么90%的“公网部署失败”其实发生在Linux系统层,而不是Java代码里;
  • 如何用3条命令判断是代码问题、端口问题、还是DNS问题;
  • 当你只有动态IP、没有公网带宽、甚至用的是校园网时,有哪些真正可用的替代方案(不是“买云服务器”这种废话);
  • 生产环境必须加的5个JVM参数,以及为什么-Xms512m -Xmx1024m在2核4G服务器上反而会拖垮整个应用。

适合谁看?

  • 写完第一个Spring Boot项目的应届生,想把简历项目链接发给面试官;
  • 小公司全栈工程师,既要写代码又要扛服务器;
  • 独立开发者,用Java做SaaS工具,需要让客户直接访问;
  • 被运维甩锅“你代码有问题”的后端同学,想自己验证到底是代码还是环境的问题。

现在,我们开始拆解这条链路。

2. 部署链路全景图:从Java进程到公网URL的7个必经关卡

很多人以为“部署=把jar包传到服务器+java -jar xxx.jar”,就像把U盘插进电脑就能听歌一样简单。但现实是:Java进程只是整条链路的第4个环节,前面3个环节不通,它连启动都没意义。下面这张图,是我给客户画过最多次的部署检查清单(已去掉所有云厂商Logo,纯技术视角):

[你的本地开发机] ↓ 1. 代码可执行性:jar包是否包含所有依赖?MANIFEST.MF里Main-Class是否正确?Spring Boot的starter版本是否与JDK匹配? ↓ 2. JVM环境就绪:JDK版本是否被识别?JAVA_HOME是否指向正确路径?PATH里是否有java命令?(注意:`java -version`显示17,不代表`/usr/bin/java`就是JDK17) ↓ 3. 系统级准备:Linux防火墙(iptables/firewalld)是否放行端口?SELinux是否禁用或配置了http_port_t?云服务器安全组规则是否生效?(重点:阿里云/腾讯云的安全组修改后需手动点击“应用”才生效) ↓ 4. Java进程启动:`java -jar app.jar --server.port=8080` 是否成功?进程是否在运行?`netstat -tuln | grep 8080` 是否显示LISTEN状态? ↓ 5. 本地验证闭环:curl http://localhost:8080/health 是否返回200?能否用`wget --no-check-certificate -qO- https://localhost:8080/health`测试HTTPS? ↓ 6. 域名与DNS:域名是否已备案(国内必需)?DNS解析记录(A记录/CNAME)是否指向服务器公网IP?TTL值是否足够低(建议300秒)以便快速回滚? ↓ 7. 反向代理与安全:Nginx/Apache是否配置了正确的proxy_pass?SSL证书是否由可信CA签发?HSTS头是否开启?HTTP是否自动跳转HTTPS?

这7个环节,环环相扣。我见过最离谱的案例:某电商后台部署失败,排查3天,最后发现是第3步——腾讯云安全组里写了“8080端口开放”,但协议类型选成了UDP而非TCP。UDP端口开着,TCP请求根本进不来,而netstat显示LISTEN,curl localhost也通,完美制造了“服务正常”的假象。

2.1 关键陷阱:你以为的“公网IP”,可能根本不是公网IP

这是新手掉进最多次的坑。运营商分配的所谓“公网IP”,实际分三种:

类型特征能否直接部署Java服务典型场景
真公网IPIPv4地址,全球路由可达,curl ifconfig.me返回的IP与服务器ip a显示一致✅ 完全可用企业专线、部分云服务器、少数家庭宽带(如上海电信)
NAT后公网IPifconfig.me返回的IP ≠ 服务器内网IP,且该IP被多个用户共享❌ 无法直接绑定端口大多数家庭宽带(移动/联通)、校园网、酒店WiFi
私有IP段ip a显示10.x.x.x / 172.16.x.x~172.31.x.x / 192.168.x.x❌ 完全不可达所有虚拟机、Docker容器、家用路由器LAN口

提示:用这三步快速验证你的IP是否真公网:

  1. 在服务器上执行curl ifconfig.me记下IP;
  2. 在另一台外网机器(如手机4G网络)执行curl http://<步骤1的IP>:8080
  3. 如果超时,再执行telnet <步骤1的IP> 8080—— 若提示Connection refused,说明端口监听但被拦截;若提示Network is unreachable,说明IP根本不可路由。

如果确认是NAT后IP(步骤2失败),别急着骂运营商。有3种成熟方案绕过:

  • 云服务器中转:租一台5元/月的轻量应用服务器(如腾讯云轻量、Vultr $2.5/月),用ssh -R 8080:localhost:8080 user@cloud-server做反向隧道;
  • P2P穿透工具:Tailscale、ZeroTier、或者国内更稳定的easytier(注意:easytier需自建derp服务器,不能只用官方节点);
  • DDNS+端口映射:如果你的路由器支持UPnP或能刷Padavan/OpenWrt,配合花生壳/Oray DDNS,把8080端口映射到路由器WAN口(但成功率低于50%,因运营商封锁严重)。

2.2 为什么必须用反向代理?直接暴露Java进程有多危险

很多教程说“java -jar app.jar --server.port=80就能直接用域名访问”,这是极其危险的操作。原因有三:

第一,端口权限问题:Linux规定1024以下端口只能由root启动。用root跑Java进程,等于把整个应用的文件读写、网络连接、进程控制权全交给JVM。一旦Spring Boot被注入恶意代码(比如Log4j2漏洞),攻击者就能直接执行rm -rf /

第二,静态资源处理低效:Spring Boot内置Tomcat处理CSS/JS/图片,QPS不到Nginx的1/10。实测:同一台2核4G服务器,Nginx代理静态资源+Spring Boot处理API,QPS 1200;纯Spring Boot处理所有请求,QPS 180。

第三,缺乏企业级防护:Nginx能轻松实现:

  • 请求限流(limit_req zone=api burst=20 nodelay);
  • IP黑名单(deny 192.168.1.100);
  • WAF基础规则(过滤<script>union select等关键词);
  • SSL/TLS卸载(让Java进程专注业务逻辑,不消耗CPU做加解密)。

所以生产环境标准姿势是:

用户浏览器 → Nginx(80/443端口) → 反向代理 → Spring Boot(8080端口,仅监听127.0.0.1)

这样,Java进程完全不暴露在公网,防火墙只需放行Nginx端口,安全边界清晰可控。

3. 实操全流程:从零开始部署一个Spring Boot项目到公网(含避坑细节)

我们以一个真实的Spring Boot 3.2 + JDK 17项目为例,完整走一遍部署流程。假设你有一台全新的腾讯云轻量应用服务器(Ubuntu 22.04,2核4G,已备案域名myapp.example.com),目标是让这个域名能访问到/api/users接口。

3.1 环境初始化:比安装JDK更重要的5件事

很多教程一上来就apt install openjdk-17-jdk,但漏掉了更关键的系统预设。这些操作看似琐碎,却决定了后续是否要重装系统:

第一步:关闭swap分区(Java应用性能杀手)

# 查看swap状态 swapon --show # 临时关闭(重启后恢复) sudo swapoff -a # 永久关闭:注释/etc/fstab里swap行 sudo sed -i '/swap/d' /etc/fstab

为什么?JVM的GC算法对swap极度敏感。当堆内存不足触发Full GC时,若部分对象被swap到磁盘,GC暂停时间(STW)会从毫秒级飙升到秒级,导致API超时。实测:同一应用,关闭swap后P99延迟下降63%。

第二步:配置时区与时间同步(避免JWT Token失效)

sudo timedatectl set-timezone Asia/Shanghai sudo systemctl enable chrony sudo systemctl start chrony # 验证 timedatectl status | grep "System clock synchronized"

注意:Spring Security OAuth2的JWT校验默认误差容忍5分钟。如果服务器时间比NTP服务器慢3分钟,而客户端快2分钟,Token直接被判定为“未生效”。

第三步:创建非root部署用户(安全基线)

sudo adduser --disabled-password --gecos "" javaapp sudo usermod -aG sudo javaapp # 切换用户并生成SSH密钥(用于后续Git拉代码) sudo -u javaapp ssh-keygen -t ed25519 -C "deploy@myapp"

绝对禁止用root用户部署!这是等保2.0三级要求。所有Java进程、Nginx配置文件、日志目录,权限必须严格分离。

第四步:配置ulimit(防止Too many open files)

# 编辑/etc/security/limits.conf echo "javaapp soft nofile 65536" | sudo tee -a /etc/security/limits.conf echo "javaapp hard nofile 65536" | sudo tee -a /etc/security/limits.conf # 使配置生效(需重新登录) sudo su - javaapp ulimit -n # 应显示65536

Spring Boot默认最大连接数是8192,但Linux单进程默认文件描述符上限是1024。当并发连接超1000,就会出现IOException: Too many open files

第五步:安装基础工具(别信“最小化安装”)

sudo apt update && sudo apt install -y \ curl wget git vim net-tools htop iotop \ nginx supervisor unzip zip

supervisor是管理Java进程的核心——它比systemd更轻量,重启后自动拉起Java服务,且日志统一归集到/var/log/supervisor/,比journalctl好排查10倍。

完成这5步,再安装JDK。此时执行:

sudo apt install -y openjdk-17-jdk-headless java -version # 输出应为 openjdk version "17.0.1"...

3.2 Java项目构建与传输:Maven打包的3个致命细节

假设你的项目GitHub地址是https://github.com/yourname/my-springboot-app,分支为main。别直接git clone,按以下顺序操作:

第一步:在服务器上创建标准部署目录结构

sudo mkdir -p /opt/myapp/{bin,conf,logs,lib} sudo chown -R javaapp:javaapp /opt/myapp # bin放启动脚本,conf放application.yml,logs放日志,lib放jar包

第二步:用Maven正确打包(关键!)

# 切换到javaapp用户 sudo -u javaapp -i # 进入项目目录(假设已clone) cd /home/javaapp/my-springboot-app # 执行标准打包命令(注意:必须加-Pprod激活生产配置) mvn clean package -Dmaven.test.skip=true -Pprod # 检查target目录 ls -lh target/*.jar # 应看到 my-springboot-app-1.0.0.jar

⚠️ 致命细节1:-Dmaven.test.skip=true不等于-DskipTests。前者跳过编译测试类,后者只跳过执行测试。CI/CD中必须用前者,否则target/test-classes会污染jar包。
⚠️ 致命细节2:Spring Boot 3.x 必须用spring-boot-maven-plugin3.2+版本,否则打包后MANIFEST.MFStart-Class缺失,java -jar直接报错no main manifest attribute
⚠️ 致命细节3:-Pprod激活的application-prod.yml里,数据库密码不能明文!必须用JVM参数传入:

spring: datasource: password: ${DB_PASSWORD:default_pass}

启动时用java -jar app.jar --DB_PASSWORD=real_pass

第三步:传输jar包(不用scp,用rsync更稳)

# 从本地推送(在你自己的电脑上执行) rsync -avz --progress \ -e "ssh -p 22" \ ./target/my-springboot-app-1.0.0.jar \ javaapp@your-server-ip:/opt/myapp/lib/

rsyncscp优势:断点续传、增量同步、传输进度可视化。当jar包超100MB时,这是救命功能。

3.3 Supervisor进程管理:让Java服务永不宕机

把jar包扔到服务器只是开始,让它持续运行才是核心。Supervisor配置如下:

创建Supervisor配置文件

sudo vim /etc/supervisor/conf.d/myapp.conf

内容:

[program:myapp] command=java -Xms512m -Xmx1024m -XX:+UseG1GC -Dfile.encoding=UTF-8 -jar /opt/myapp/lib/my-springboot-app-1.0.0.jar --spring.profiles.active=prod --server.port=8080 --logging.file.name=/opt/myapp/logs/app.log directory=/opt/myapp user=javaapp autostart=true autorestart=true startretries=3 stderr_logfile=/var/log/supervisor/myapp_error.log stdout_logfile=/var/log/supervisor/myapp_access.log environment=JAVA_HOME="/usr/lib/jvm/java-17-openjdk-amd64"

关键参数解析

  • autorestart=true:进程崩溃后自动重启(比systemd Restart=always更可靠);
  • startretries=3:启动失败重试3次,避免因数据库未就绪导致无限重启;
  • environment:显式声明JAVA_HOME,防止Supervisor找不到JDK;
  • -Xms512m -Xmx1024m:堆内存初始值=最大值,避免运行时扩容GC;
  • -XX:+UseG1GC:JDK9+默认GC,但显式声明更稳妥;
  • --logging.file.name:日志路径必须绝对路径,且/opt/myapp/logs/目录需提前chown javaapp

加载并启动

sudo supervisorctl reread sudo supervisorctl update sudo supervisorctl start myapp # 查看状态 sudo supervisorctl status # 实时查看日志 sudo tail -f /var/log/supervisor/myapp_access.log

实操心得:Supervisor日志默认不轮转!生产环境必须加logrotate:

sudo vim /etc/logrotate.d/supervisor # 内容:/var/log/supervisor/*.log { daily rotate 30 compress missingok }

3.4 Nginx反向代理:5行配置搞定HTTPS与负载均衡

Nginx配置是公网访问的“最后一公里”。在/etc/nginx/sites-available/myapp中写:

upstream backend { server 127.0.0.1:8080 max_fails=3 fail_timeout=30s; # 如需负载均衡,可加第二台:server 192.168.1.101:8080; } 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/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; 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_read_timeout 60; proxy_connect_timeout 60; } # 静态资源缓存(可选) location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { expires 1y; add_header Cache-Control "public, immutable"; } }

启用配置

sudo ln -sf /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/ sudo nginx -t # 测试语法 sudo systemctl reload nginx

申请SSL证书(Let's Encrypt)

# 安装certbot sudo apt install -y certbot python3-certbot-nginx # 获取证书(需确保域名DNS已解析到服务器IP) sudo certbot --nginx -d myapp.example.com # 自动续期(certbot会自动添加crontab) sudo crontab -l | grep certbot

注意:certbot申请证书时,会自动修改Nginx配置添加.well-known/acme-challenge路径。如果之前手动改过Nginx配置,务必先备份/etc/nginx/sites-available/myapp

3.5 全链路验证:用5个命令定位90%的问题

部署完成后,不要直接打开浏览器。按顺序执行这5个命令,每一步都必须成功:

  1. 验证Java进程是否真在监听

    sudo -u javaapp ss -tuln | grep :8080 # 正确输出:tcp LISTEN 0 100 127.0.0.1:8080 *:* # 错误输出:无结果 → Java没启动;显示0.0.0.0:8080 → 配置了server.address=0.0.0.0,有安全风险
  2. 验证Nginx能否代理到Java

    curl -H "Host: myapp.example.com" http://127.0.0.1:80 # 应返回Spring Boot的欢迎页或API JSON,且HTTP状态码200
  3. 验证HTTPS证书是否有效

    openssl s_client -connect myapp.example.com:443 -servername myapp.example.com 2>/dev/null | openssl x509 -noout -dates # 输出应显示notAfter日期在1年以后
  4. 验证DNS解析是否生效

    dig +short myapp.example.com # 返回值必须等于你的服务器公网IP # 如果返回空,检查域名DNS服务商(阿里云DNS/Cloudflare)的A记录
  5. 模拟真实用户访问(终极验证)

    # 用curl模拟手机微信内置浏览器 curl -H "User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.40(0x18002833) NetType/WIFI Language/zh_CN" \ https://myapp.example.com/api/users

常见失败场景速查:

  • 命令1失败 → 检查Supervisor日志/var/log/supervisor/myapp_error.log
  • 命令2成功但命令5失败 → 检查云服务器安全组是否放行443端口;
  • 命令4失败但命令2成功 → DNS未生效,等待TTL时间或刷新本地DNS缓存(sudo systemd-resolve --flush-caches)。

4. 高阶实战:没有公网IP、校园网、动态IP的3种破局方案

上面的流程基于“有真公网IP”的理想情况。但现实中,90%的个人开发者和小团队面临的是更复杂的网络环境。以下是经过千次实测的3种方案,附具体命令和成本对比:

4.1 方案一:Tailscale + 自建DERP服务器(推荐指数★★★★★)

Tailscale是目前最稳定的P2P穿透方案,但官方DERP节点在国内延迟高。自建DERP能将延迟从800ms降到50ms以内。

部署步骤

  1. 在腾讯云轻量服务器(5元/月)上安装DERP:

    # 下载二进制 wget https://github.com/tailscale/tailscale/releases/download/v1.62.0/tailscale-1.62.0.amd64.tgz tar -xzf tailscale-1.62.0.amd64.tgz sudo cp tailscale/bin/derper /usr/local/bin/ # 创建DERP配置 sudo mkdir -p /etc/derper echo '{ "Regions": { "900": { "RegionID": 900, "RegionCode": "cn", "RegionName": "China", "Nodes": [{ "Name": "derp1", "RegionID": 900, "HostName": "derp.cn.example.com", "DerpMap": true }] } } }' | sudo tee /etc/derper/derper.json
  2. 启动DERP服务(用systemd托管):

    sudo vim /etc/systemd/system/derper.service # 内容: [Unit] Description=Tailscale DERP Server After=network.target [Service] Type=simple User=root ExecStart=/usr/local/bin/derper -hostname=derp.cn.example.com -stun -certmode=letsencrypt -certdir=/var/lib/derper/certs Restart=on-failure RestartSec=10 [Install] WantedBy=multi-user.target
    sudo systemctl daemon-reload sudo systemctl enable derper sudo systemctl start derper
  3. 在你的Java服务器上安装Tailscale客户端,并指定自建DERP:

    curl -fsSL https://tailscale.com/install.sh | sh sudo tailscale up --login-server=https://login.tailscale.com --derp-map-url=https://derp.cn.example.com/derpmap.json
  4. 配置Nginx反向代理到Tailscale IP(如100.64.0.123):

    upstream backend { server 100.64.0.123:8080; # 不再用127.0.0.1! }

成本:仅需1台云服务器(5元/月)+ 域名(约30元/年)。
优势:无需改Java代码,所有流量加密,支持IPv6,Tailscale自动处理NAT穿越。
注意:derp.cn.example.com必须是已备案域名,且DNS A记录指向云服务器IP。

4.2 方案二:frp内网穿透(适合无备案域名场景)

frp是国产开源方案,配置比Tailscale更灵活,但需自行维护服务端。

服务端(云服务器)部署

# 下载frps(服务端) wget https://github.com/fatedier/frp/releases/download/v0.54.0/frp_0.54.0_linux_amd64.tar.gz tar -xzf frp_0.54.0_linux_amd64.tar.gz cd frp_0.54.0_linux_amd64 # 编辑frps.ini echo '[common] bind_port = 7000 kcp_bind_port = 7000 dashboard_port = 7500 dashboard_user = admin dashboard_pwd = your_password vhost_http_port = 80 vhost_https_port = 443' | sudo tee frps.ini # 启动 nohup ./frps -c frps.ini > /var/log/frps.log 2>&1 &

客户端(你的Java服务器)部署

# 下载frpc(客户端) wget https://github.com/fatedier/frp/releases/download/v0.54.0/frp_0.54.0_linux_amd64.tar.gz tar -xzf frp_0.54.0_linux_amd64.tar.gz cd frp_0.54.0_linux_amd64 # 编辑frpc.ini echo '[common] server_addr = your-cloud-ip server_port = 7000 [web] type = http local_port = 8080 custom_domains = myapp.example.com' | sudo tee frpc.ini # 启动 nohup ./frpc -c frpc.ini > /var/log/frpc.log 2>&1 &

优势:支持自定义子域名,HTTP/HTTPS/FTP/TCP全协议穿透。
劣势:frp服务端需自行监控,无Web管理界面(需用curl http://your-cloud-ip:7500登录dashboard)。

4.3 方案三:Cloudflare Tunnel(零配置,但有功能限制)

Cloudflare Tunnel是真正的“开箱即用”方案,适合不想碰服务器命令的开发者。

步骤

  1. 在Cloudflare控制台添加域名,将DNS设置为“Proxied”(橙色云朵);
  2. 在Java服务器上安装cloudflared:
    curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64 -o cloudflared chmod +x cloudflared sudo mv cloudflared /usr/local/bin/
  3. 登录并创建Tunnel:
    cloudflared tunnel login cloudflared tunnel create myapp-tunnel # 记下生成的tunnel ID
  4. 创建配置文件/etc/cloudflared/config.yml
    tunnel: <your-tunnel-id> credentials-file: /root/.cloudflared/<your-tunnel-id>.json ingress: - hostname: myapp.example.com service: http://localhost:8080 - service: http_status:404
  5. 启动服务:
    cloudflared service install sudo systemctl start cloudflared

优势:完全免费,自动HTTPS,DDoS防护,无需开任何端口。
劣势:仅支持HTTP/HTTPS,不支持WebSocket长连接(Spring Boot的SSE会断连);日志需在Cloudflare Dashboard查看,无法本地分析。

5. 生产环境必须做的10项加固与监控(附检查清单)

部署成功只是开始,生产环境的稳定性取决于这10件事。我把它做成一张可打印的检查清单,每次上线前逐项核对:

序号检查项操作命令/方法不通过后果我的实操备注
1JVM堆内存是否合理jstat -gc <pid>查看S0C/S1C/EC/OCOOM频繁,Full GC每分钟10次以上建议:-Xms=-Xmx,且≤物理内存50%
2日志是否异步写入检查logback-spring.xml是否有<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">磁盘IO打满,API响应变慢必须用RollingFileAppender,禁用ConsoleAppender
3数据库连接池是否配置show variables like 'max_connections';对比spring.datasource.hikari.maximum-pool-size连接数耗尽,大量HikariPool-1 - Connection is not availableHikariCP最大连接数≤MySQL max_connections×0.8
4Nginx是否启用Gzipcurl -H "Accept-Encoding: gzip" -I http://myapp.example.com查看Content-Encoding: gzip首屏加载慢200%,移动端流量翻倍添加gzip on; gzip_types application/json text/css;
5SSL是否启用HSTScurl -I https://myapp.example.com | grep Strict-Transport-SecurityHTTPS可被降级为HTTP,中间人攻击风险Nginx配置加add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
6是否配置健康检查端点curl http://localhost:8080/actuator/healthKubernetes/LB无法感知服务状态,故障不自动剔除Spring Boot 3.x需加management.endpoints.web.exposure.include=health,info,metrics
7文件上传大小是否限制curl -F "file=@large.zip" https://myapp.example.com/upload上传大文件导致OOM或超时Nginx加client_max_body_size 100M;,Spring加spring.servlet.multipart.max-file-size=100MB
8是否禁用HTTP TRACE方法curl -X TRACE http://myapp.example.comHTTP TRACE可被利用做跨站追踪攻击Nginx加if ($request_method !~ ^(GET|HEAD|POST|PUT|DELETE|OPTIONS)$) { return 405; }
9Linux内核参数是否优化sysctl net.core.somaxconn应≥65535并发连接数超1000时大量Connection reset by peerecho 'net.core.somaxconn = 65535' >> /etc/sysctl.conf
10是否配置告警通知curl -X POST https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=xxx -H 'Content-Type: application/json' -d '{"msgtype": "text", "text": {"content": "服务异常"}}'故障发生2小时后才发现推荐用Prometheus+Alertmanager+企业微信

最后分享一个血泪教训:某次大促前,我们漏了第9项(somaxconn),凌晨3点突增流量,Nginx错误日志疯狂刷accept() failed (24: Too many open files),紧急扩容3台服务器才扛住。后来我把这10项做成Ansible