开发者

SpringBoot实现应用开机自启动与进程守护配置方案

开发者 https://www.devze.com 2025-07-11 10:43 出处:网络 作者: 风象南
目录一个故(shi)事(gu)方案一:systemd配置(推荐)什么是systemd?实战步骤:从0到1配置1. 准备SpringBoot应用2. 创建启动脚本(关键)3. 创建systemd服务文件4. 服务管理命令避坑指南坑1:权限问题导致启动失败
目录
  • 一个故(shi)事(gu)
  • 方案一:systemd配置(推荐)
    • 什么是systemd?
    • 实战步骤:从0到1配置
      • 1. 准备SpringBoot应用
      • 2. 创建启动脚本(关键)
      • 3. 创建systemd服务文件
      • 4. 服务管理命令
    • 避坑指南
      • 坑1:权限问题导致启动失败
      • 坑2:应用启动慢导致超时
      • 坑3:优雅关闭不生效
  • 方案二:Supervisor配置(兼容更多系统)
    • 安装Supervisor
      • 创建应用配置文件
        • Supervisor管理命令
        • 两种方案对比与选择建议
          • 高级配置:健康检查与自动恢复
            • systemd健康检查
              • 防止无限重启策略
              • 总结

                一个故(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 启动成功!android"
                        echo "日志文件:$LOG_FILE"
                        exit 0
                    fi
                    sleep 1
                done
                
                # 启动超时处理
                echo "错误:$APP_NAME 启动超时!"
                echo "查看日志获取详细信息:tail -f $LOG_FjavascriptILE"
                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 restarPSfzJaqfgt 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.lwww.devze.comog ]; 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 systemcjavascripttl 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掉后自动恢复的能力。

                以上就是SpringBoot实现应用开机自启动与进程守护配置方案的详细内容,更多关于SpringBoot应用开机自启动与进程守护的资料请关注编程客栈(www.devze.com)其它相关文章!

                0

                精彩评论

                暂无评论...
                验证码 换一张
                取 消

                关注公众号