芋道后端部署后总自己挂?从 Nginx 报错到 OOM Kill 的完整排查与修复(2核2G 服务器实战)

环境: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 记录

九、额外优化建议(按优先级分层推进)

  1. 模块精简

    不用的模块先禁用(尤其是定时任务、报表引擎、工作流等)。模块越少,常驻内存与线程越少。

  2. 外部化中间件

    Redis/MySQL 尽量使用托管或外部服务,减少本机内存争抢。

  3. Tomcat 线程与连接收敛

    Spring Boot 配置:

    properties 复制代码
    server.tomcat.accept-count=100
    server.tomcat.max-connections=200
    server.tomcat.threads.max=100

    降低瞬时并发引发的内存冲击。

  4. 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
  5. 升级规格

    如果业务持续增长,尽快升级至 4G/8G,或做水平拆分(前后端/功能模块/微服务化)。


十、从零到一的完整"复现型"操作清单

  1. 确认问题面

    • Nginx error.log 出现 111: Connection refused
    • ss -lnt | grep 9999 发现端口无人监听
    • dmesg | grep -i kill 看到 Out of memory: Killed process (java)
  2. 立刻止血

    • 启动命令将 -Xmx 调为 512M(或更低),限制 MaxMetaspaceSize
    • 增加 2G Swap
  3. 规范化启动

    • 使用 systemd 管理后端,Restart=always
    • 统一日志与工作目录
  4. 回归验证

    • 连续访问/压测 30~60 分钟
    • 无新的 111OOM,内存曲线稳定
  5. 持续优化

    • 精简模块、外部化中间件、限制 Tomcat 并发
    • 必要时加机器或拆分

结论

  • 症状 :Nginx 的 111: Connection refused 不是网络问题,而是后端进程已不在
  • 根因 :2 核 2G 环境中,后端引入模块多、JVM 最大堆过大,导致系统 OOM 杀死 Java。
  • 解法 :限制 JVM 堆与元空间,-Xmx≤512M ;增加 2G Swap 兜底;使用 systemd 规范化守护;验证与持续优化。
相关推荐
云边有个稻草人2 小时前
Windows 里用 Linux 不卡顿?WSL + cpolar让跨系统开发变简单
linux·运维·服务器·cpolar
LXY_BUAA3 小时前
将linux操作系统装入U盘20251107
linux·运维·服务器
IDC02_FEIYA3 小时前
Discuz论坛管理员怎么重置修改用户密码?
运维·服务器
九河云3 小时前
华为云ECS与Flexus云服务器X实例:差异解析与选型指南
大数据·运维·服务器·网络·人工智能·华为云
kaoa0004 小时前
Linux入门攻坚——53、drbd - Distribute Replicated Block Device,分布式复制块设备-2
linux·运维·服务器
ajax_beijing4 小时前
华为云ELB
运维·服务器·华为云
RisunJan4 小时前
Linux命令-e2label命令(设置第二扩展文件系统的卷标)
linux·运维·服务器
倦王4 小时前
Linux一些基本命令--黑马学习
linux·运维·服务器
王道长服务器 | 亚马逊云4 小时前
AWS + 苹果CMS:影视站建站的高效组合方案
服务器·数据库·搜索引擎·设计模式·云计算·aws