环境:2 核 2G 内存、3M 带宽;宝塔面板;Nginx 反向代理到 Spring Boot(示例端口:
9999);项目为 芋道 RuoYi-Vue-Pro (禁用 IoT,其余模块基本都引入)。现象:前端偶发 502/超时,Nginx 报
connect() failed (111: Connection refused),后端 Java 服务"运行一会儿就自己没了"。
一、先从现象入手:Nginx 报 111: Connection refused
典型日志(路径示例):
/www/server/nginx/logs/error.log
报错示例:
connect() failed (111: Connection refused) while connecting to upstream,
upstream: "http://127.0.0.1:9999/..."
含义:Nginx 把请求转发给 127.0.0.1:9999 时被拒绝连接,通常是后端端口没有进程在监听(进程已退出/被杀)。
快速核对命令:
bash
# 实时观察 Nginx 错误
tail -f /www/server/nginx/logs/error.log
# 端口是否在监听
ss -lnt | grep 9999
# Java 进程是否存在
ps -ef | grep -w java
如果端口没人监听、Java 进程也不在,说明应用"掉了"。
二、确认应用是否正常启动过:看后端应用日志
宝塔常见日志路径(示例):
/www/wwwlogs/java/springboot/app.log
# 或你在启动脚本中自定义的路径
需要看到诸如:
Tomcat started on port 9999
Started XxxApplication ...
项目启动成功!
完成请求 URL(/admin-api/xxx)
如果确实启动成功并处理过请求,但后来无异常栈直接"消失",高度怀疑是系统 OOM(内存不足)把 Java 进程杀掉。
三、查系统"黑匣子":是否 OOM Kill(关键证据)
命令:
bash
dmesg | grep -i -E "killed process|oom-killer|out of memory"
若出现类似:
Out of memory: Killed process 631514 (java) total-vm:4214016kB, ...
task_memcg=/system.slice/spring_xxx.service, task=java
则已坐实:内存不足导致内核 OOM Killer 杀死 Java 进程 ,于是 Nginx 转发到后端就报 111: Connection refused。
四、为什么会 OOM:2 核 2G 机器的内存账本
粗略占用参考(不同场景会有差异):
- JVM 堆(
-Xmx):500MB ~ 1GB(默认常常偏大) - JVM 元空间/线程栈/DirectBuffer:150MB ~ 400MB
- 系统/宝塔/Nginx/Redis/MySQL 等:300MB ~ 600MB
- 项目模块越多(Flowable、报表、支付、消息等),常驻内存越高
在 2G 机器上,轻易就会达到临界点,一有波动就触发 OOM。
五、立刻止血:限制 JVM 内存 + 增加 Swap 兜底
1)限制 JVM 内存(核心)
把最大堆明显降下来(建议 2G 机器 ≤ 512M),同时限制元空间。
启动命令模板(示例路径与端口,自行替换):
bash
/usr/lib/jvm/jdk-17/bin/java \
-Dspring.profiles.active=dev \
-Xms256M \
-Xmx512M \
-XX:MaxMetaspaceSize=128M \
-XX:+UseG1GC \
-XX:+UseStringDeduplication \
-jar /var/www/project/yudao-server.jar \
--server.port=9999
-Xms256M:初始堆-Xmx512M:最大堆(别贪大)-XX:MaxMetaspaceSize=128M:限制类元空间G1GC + StringDeduplication:对服务侧较友好,降低堆压力
如果你原来是
-Xmx1024M(1G),在 2G 机器上几乎是 OOM 定时炸弹,务必调小。
2)增加 Swap(建议至少 2G)
Swap 不提升性能,但能防止关键进程"被一刀切"。
bash
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
free -h # 确认 Swap 生效
六、Nginx 反代与后端的常用检查
1)Nginx 反代配置(关键片段示例)
nginx
location / {
proxy_pass http://127.0.0.1:9999;
proxy_http_version 1.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 5s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
2)确认后端"真的在监听 9999"
bash
ss -lnt | grep 9999
3)健康探测(可选)
在 Spring Boot 暴露 actuator/health,Nginx 可做简易探测与容错转发。
七、宝塔/系统服务(systemd)规范化启动
推荐用 systemd 管理后端,崩了能自动重启并统一日志位置。
示例 unit 文件: /etc/systemd/system/yudao-server.service
ini
[Unit]
Description=Yudao Server
After=network.target
[Service]
User=root
WorkingDirectory=/var/www/project
ExecStart=/usr/lib/jvm/jdk-17/bin/java \
-Dspring.profiles.active=dev \
-Xms256M -Xmx512M -XX:MaxMetaspaceSize=128M \
-XX:+UseG1GC -XX:+UseStringDeduplication \
-jar /var/www/project/yudao-server.jar \
--server.port=9999
Restart=always
RestartSec=5
LimitNOFILE=65535
# 可选:限制内存上限,避免误配导致挤爆(按需设置)
# MemoryMax=1.2G
[Install]
WantedBy=multi-user.target
启用与查看:
bash
systemctl daemon-reload
systemctl enable yudao-server
systemctl start yudao-server
systemctl status yudao-server -n 100
journalctl -u yudao-server -f
八、验证与回归
1)确认 JVM 参数生效
bash
jcmd $(pidof java) VM.flags 2>/dev/null
2)观察内存与进程稳定性
bash
top -p $(pidof java)
ps -eo pid,comm,%mem,%cpu --sort=-%mem | head
3)关注是否还有 111: Connection refused
bash
tail -f /www/server/nginx/logs/error.log
4)压测/访问一段时间后,再次确认
- Java 进程仍在
- 9999 端口仍监听
- Nginx 不再出现
111 dmesg无新增 OOM 记录
九、额外优化建议(按优先级分层推进)
-
模块精简
不用的模块先禁用(尤其是定时任务、报表引擎、工作流等)。模块越少,常驻内存与线程越少。
-
外部化中间件
Redis/MySQL 尽量使用托管或外部服务,减少本机内存争抢。
-
Tomcat 线程与连接收敛
Spring Boot 配置:
propertiesserver.tomcat.accept-count=100 server.tomcat.max-connections=200 server.tomcat.threads.max=100降低瞬时并发引发的内存冲击。
-
GC 日志/Heap Dump(高级排查)
若仍不稳,可临时打开 GC 日志与 OOM Dump 定位热区(仅在排查期间打开,避免额外 IO):
bash-XX:+ExitOnOutOfMemoryError -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/heapdump.hprof -Xlog:gc*:file=/var/log/gc.log:tags,uptime,time,level -
升级规格
如果业务持续增长,尽快升级至 4G/8G,或做水平拆分(前后端/功能模块/微服务化)。
十、从零到一的完整"复现型"操作清单
-
确认问题面
- Nginx
error.log出现111: Connection refused ss -lnt | grep 9999发现端口无人监听dmesg | grep -i kill看到Out of memory: Killed process (java)
- Nginx
-
立刻止血
- 启动命令将
-Xmx调为512M(或更低),限制MaxMetaspaceSize - 增加 2G Swap
- 启动命令将
-
规范化启动
- 使用 systemd 管理后端,
Restart=always - 统一日志与工作目录
- 使用 systemd 管理后端,
-
回归验证
- 连续访问/压测 30~60 分钟
- 无新的
111、OOM,内存曲线稳定
-
持续优化
- 精简模块、外部化中间件、限制 Tomcat 并发
- 必要时加机器或拆分
结论
- 症状 :Nginx 的
111: Connection refused不是网络问题,而是后端进程已不在。 - 根因 :2 核 2G 环境中,后端引入模块多、JVM 最大堆过大,导致系统 OOM 杀死 Java。
- 解法 :限制 JVM 堆与元空间,
-Xmx≤512M;增加 2G Swap 兜底;使用 systemd 规范化守护;验证与持续优化。