SpringBoot应用开机自启动与进程守护配置

一个故(shi)事(gu)

"小王,生产环境怎么回事?服务全挂了!"

上个月一个周六的凌晨,小王被这通电话惊醒。

机房意外断电,恢复供电后所有服务器重启,但我们的SpringBoot应用没有自动启动,导致业务中断了整整40分钟。

事后排查发现,是之前的运维同事离职时没有留下开机自启动配置文档。更糟的是,应用运行中偶尔会因为OOM崩溃,也没有任何进程守护机制。

经历这次事故后,小王整理了两套SpringBoot应用开机自启动与进程守护方案,在生产环境稳定运行至今。

方案一:systemd配置(推荐)

什么是systemd?

简单说,systemd是现在Linux系统的"大管家",负责管理系统启动和服务进程。

几乎所有主流Linux(CentOS 、Ubuntu 、Debian等)都支持使用systemd。

为什么推荐它?因为:

原生集成:不需要额外安装软件 功能强大:支持开机自启、进程监控、日志管理 配置简单:一个服务文件搞定所有设置

实战步骤:从0到1配置
1. 准备SpringBoot应用

假设我们的应用信息:

  • 应用名称:demo-app
  • JAR文件路径:/opt/apps/demo-app.jar
  • 运行用户:appuser(建议使用非root用户)
  • 日志路径:/var/log/demo-app.log
2. 创建启动脚本(关键)

不要直接在systemd中调用java命令!创建一个启动脚本/opt/apps/start.sh


#!/bin/bash # SpringBoot应用启动脚本 APP_NAME="demo-app" JAR_FILE="/opt/apps/demo-app.jar" LOG_FILE="/var/log/${APP_NAME}.log" JVM_OPTS="-Xms512m -Xmx512m -XX:+UseG1GC -XX:MaxGCPauseMillis=200" SERVER_PORT=8080 # 检查JAR文件是否存在 if [ ! -f "$JAR_FILE" ]; then echo "错误:JAR文件 $JAR_FILE 不存在!" exit 1 fi # 检查端口是否被占用 check_port() { netstat -tlnp | grep ":$SERVER_PORT " > /dev/null return $? } if check_port; then echo "错误:端口 $SERVER_PORT 已被占用!" netstat -tlnp | grep ":$SERVER_PORT " exit 1 fi # 启动应用 echo "开始启动 $APP_NAME ..." nohup java $JVM_OPTS -jar $JAR_FILE --server.port=$SERVER_PORT > $LOG_FILE 2>&1 & # 等待应用启动 echo "等待应用启动,端口:$SERVER_PORT ..." for i in {1..30}; do if check_port; then echo "$APP_NAME 启动成功!" echo "日志文件:$LOG_FILE" exit 0 fi sleep 1 done # 启动超时处理 echo "错误:$APP_NAME 启动超时!" echo "查看日志获取详细信息:tail -f $LOG_FILE" exit 1

给脚本添加执行权限:


chmod +x /opt/apps/start.sh

3. 创建systemd服务文件

创建/etc/systemd/system/demo-app.service


[Unit] Description=Demo SpringBoot Application Documentation=https://example.com/docs After=network.target mysql.service # 依赖网络和MySQL服务启动后再启动 Wants=mysql.service # MySQL启动失败不影响本服务启动 [Service] User=appuser Group=appuser WorkingDirectory=/opt/apps ExecStart=/opt/apps/start.sh # 自定义停止命令(优雅关闭) ExecStop=/bin/bash -c "PID=$(pgrep -f 'demo-app.jar'); if [ -n "$PID" ]; then kill -15 $PID; sleep 10; if ps -p $PID > /dev/null; then kill -9 $PID; fi; fi" # 启动前备份日志 ExecStartPre=/bin/bash -c "if [ -f /var/log/demo-app.log ]; then mv /var/log/demo-app.log /var/log/demo-app.log.$(date +%%Y%%m%%d%%H%%M%%S); fi" SuccessExitStatus=143 # 识别SpringBoot优雅关闭状态码 Restart=always # 总是重启(异常退出时) RestartSec=5 # 重启间隔5秒 TimeoutStartSec=300 # 启动超时时间(5分钟) TimeoutStopSec=60 # 停止超时时间 Environment="SPRING_PROFILES_ACTIVE=prod" # 环境变量 Environment="JAVA_HOME=/usr/lib/jvm/java-11-openjdk" [Install] WantedBy=multi-user.target # 多用户模式下启动

4. 服务管理命令

# 重载systemd配置(修改服务文件后必须执行) sudo systemctl daemon-reload # 启动服务 sudo systemctl start demo-app # 停止服务 sudo systemctl stop demo-app # 重启服务 sudo systemctl restart demo-app # 设置开机自启 sudo systemctl enable demo-app # 取消开机自启 sudo systemctl disable demo-app # 查看服务状态 sudo systemctl status demo-app # 查看服务日志 sudo journalctl -u demo-app -f # -f表示实时跟踪

状态检查示例


demo-app.service - Demo SpringBoot Application Loaded: loaded (/etc/systemd/system/demo-app.service; enabled; vendor preset: disabled) Active: active (running) since Wed 2023-11-15 10:30:00 CST; 5min ago Docs: https://example.com/docs Process: 1234 ExecStartPre=/bin/bash -c if [ -f /var/log/demo-app.log ]; then mv /var/log/demo-app.log /var/log/demo-app.log.20231115102959; fi (code=exited, status=0/SUCCESS) Main PID: 1245 (start.sh) Tasks: 30 (limit: 4915) Memory: 256.0M CGroup: /system.slice/demo-app.service ├─1245 /bin/bash /opt/apps/start.sh └─1250 java -Xms512m -Xmx512m -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -jar /opt/apps/demo-app.jar --server.port=8080

避坑指南
坑1:权限问题导致启动失败

现象Job for demo-app.service failed because the control process exited with error code.
解决:确保appuser用户有JAR文件和日志目录的权限


sudo chown -R appuser:appuser /opt/apps /var/log/demo-app.log

坑2:应用启动慢导致超时

现象Failed to start Demo SpringBoot Application. Timeout was reached.
解决:增加启动超时时间


[Service] TimeoutStartSec=300 # 5分钟

坑3:优雅关闭不生效

现象:执行stop后应用立即退出,@PreDestroy方法未执行
解决:配置优雅关闭参数


[Service] KillSignal=SIGTERM TimeoutStopSec=60

同时在SpringBoot配置文件中添加:


server: shutdown: graceful spring: lifecycle: timeout-per-shutdown-phase: 30s

方案二:Supervisor配置(兼容更多系统)

如果你的服务器是较老的Linux发行版(如CentOS 6),或者需要管理多个进程,Supervisor是个不错的选择。

它是Python编写的进程管理工具,跨平台性好。

安装Supervisor

CentOS安装


# 安装EPEL源 sudo yum install -y epel-release # 安装Supervisor sudo yum install -y supervisor # 启动并设置开机自启 sudo systemctl start supervisord sudo systemctl enable supervisord

Ubuntu安装


sudo apt-get update sudo apt-get install -y supervisor

创建应用配置文件

创建/etc/supervisord.d/demo-app.ini


[program:demo-app] command=/usr/bin/java -Xms512m -Xmx512m -XX:+UseG1GC -jar /opt/apps/demo-app.jar --server.port=8080 directory=/opt/apps user=appuser autostart=true # 随Supervisor启动而启动 autorestart=true # 进程退出时自动重启 startretries=3 # 启动失败重试次数 startsecs=10 # 启动后稳定运行10秒才算成功 redirect_stderr=true # 错误日志重定向到标准输出 stdout_logfile=/var/log/demo-app.log stdout_logfile_maxbytes=10MB # 日志文件大小限制 stdout_logfile_backups=10 # 日志备份数量 environment= SPRING_PROFILES_ACTIVE="prod", JAVA_HOME="/usr/lib/jvm/java-11-openjdk"

Supervisor管理命令

# 重载配置(修改配置后执行) sudo supervisorctl update # 查看所有进程状态 sudo supervisorctl status # 启动应用 sudo supervisorctl start demo-app # 停止应用 sudo supervisorctl stop demo-app # 重启应用 sudo supervisorctl restart demo-app # 查看应用日志 sudo supervisorctl tail -f demo-app

实战经验: "Supervisor的日志轮转功能非常实用,但要注意单位是MB不是GB!另外,它本身也需要通过systemd来管理开机启动,有点套娃的感觉。"

两种方案对比与选择建议

特性systemdSupervisor
系统集成原生集成,无需额外安装需要单独安装
配置复杂度中等简单
进程监控支持支持
多应用管理配置文件分散集中管理
跨平台性仅LinuxLinux/Unix/Mac

使用建议

生产环境首选systemd:原生、轻量、功能足够 多应用管理选Supervisor:集中配置,适合管理多个微服务 Docker环境不需要:容器本身提供进程管理

高级配置:健康检查与自动恢复

systemd健康检查

创建健康检查脚本/opt/apps/healthcheck.sh


#!/bin/bash PORT=8080 TIMEOUT=5 # 检查健康端点 result=$(curl -s -w "%{http_code}" -o /dev/null --connect-timeout $TIMEOUT http://localhost:$PORT/actuator/health) if [ "$result" -eq 200 ]; then exit 0 else echo "健康检查失败,HTTP状态码: $result" exit 1 fi

修改服务文件添加健康检查


[Service] HealthCheckSec=30 # 每30秒检查一次 HealthCheckCmd=/opt/apps/healthcheck.sh HealthCheckFailureAction=restart # 检查失败则重启

SpringBoot需暴露健康端点


management: endpoints: web: exposure: include: health endpoint: health: show-details: always probes: enabled: true

"健康检查是防止'僵尸进程'的有效手段。我们可能会遇到应用进程存在但无法提供服务的情况,有了健康检查后,systemd会自动重启这类异常应用。"

防止无限重启策略

无论是systemd还是Supervisor,都需要配置重启限制,避免应用陷入重启循环:

systemd配置


[Unit] StartLimitInterval=600 # 10分钟内 StartLimitBurst=5 # 最多重启5次 StartLimitAction=poweroff # 达到限制后关闭系统(极端情况)

Supervisor配置


[program:demo-app] autorestart=unexpected # 仅意外退出时重启 exitcodes=0,143 # 正常退出状态码

总结

按照这些步骤配置后,你的SpringBoot应用将具备服务器重启或应用异常down掉后自动恢复的能力。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值