ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Spring Boot应用启停脚本设计与实现指南

Spring Boot应用启停脚本设计与实现指南 1. 应用启停脚本的必要性与设计思路在项目部署运维中规范的启停脚本是保障服务可靠性的第一道防线。我经历过太多因为手工操作不规范导致的半夜报警——要么是nohup忘记写日志重定向导致磁盘爆满要么是kill -9误杀关键进程。一套健壮的启停脚本应该实现以下核心目标标准化操作流程统一启动参数、环境变量、日志目录等配置异常处理机制捕获进程异常退出并自动恢复状态可观测性提供清晰的进程状态查询接口多环境适配兼容Linux/Windows等不同操作系统以典型的Spring Boot应用为例下面这个目录结构是我们团队的标准实践/myapp ├── bin/ # 脚本目录 │ ├── start.sh # Linux启动脚本 │ ├── stop.sh │ ├── restart.sh │ └── status.sh ├── conf/ # 配置文件 ├── logs/ # 日志文件 └── lib/ # 依赖jar包2. Linux环境脚本实现详解2.1 基础启动脚本实现先看一个基础版的start.sh实现#!/bin/bash APP_NAMEmyapp JAR_PATH../lib/${APP_NAME}.jar LOG_PATH../logs/${APP_NAME}.log PID_FILE../logs/${APP_NAME}.pid # 检查是否已运行 if [ -f $PID_FILE ]; then echo Error: $APP_NAME is already running! exit 1 fi # 启动应用 nohup java -Xms512m -Xmx1024m -jar $JAR_PATH $LOG_PATH 21 echo $! $PID_FILE echo $APP_NAME started with PID $(cat $PID_FILE)关键点说明nohup保证终端关闭后进程继续运行21将标准错误重定向到标准输出$!获取最后启动的后台进程PID将PID写入文件供后续管理使用2.2 增强版启动脚本基础版存在几个明显缺陷没有JVM参数配置缺少启动成功校验日志文件可能无限增长改进后的版本#!/bin/bash # 环境检查 [ ! -d ../logs ] mkdir ../logs # 配置区 APP_NAMEmyapp JAVA_OPTS-Xms512m -Xmx1024m -XX:UseG1GC -Dfile.encodingUTF-8 SPRING_OPTS--spring.profiles.activeprod JAR_PATH../lib/${APP_NAME}.jar LOG_PATH../logs/${APP_NAME}.log PID_FILE../logs/${APP_NAME}.pid # 日志轮转 LOG_MAX_SIZE100M if [ -f $LOG_PATH ]; then CURRENT_SIZE$(du -m $LOG_PATH | cut -f1) [ $CURRENT_SIZE -gt ${LOG_MAX_SIZE/M/} ] mv $LOG_PATH $LOG_PATH.$(date %Y%m%d%H%M) fi # 启动流程 echo Starting $APP_NAME... nohup java $JAVA_OPTS -jar $JAR_PATH $SPRING_OPTS $LOG_PATH 21 # 启动校验 sleep 5 if ! ps -p $! /dev/null; then echo Startup failed! Check log: $LOG_PATH exit 1 fi echo $! $PID_FILE echo $APP_NAME started with PID $(cat $PID_FILE)2.3 配套管理脚本实现完整的服务管理需要配套脚本stop.sh#!/bin/bash APP_NAMEmyapp PID_FILE../logs/${APP_NAME}.pid if [ ! -f $PID_FILE ]; then echo Error: PID file not found! exit 1 fi PID$(cat $PID_FILE) kill $PID rm -f $PID_FILE echo $APP_NAME stoppedstatus.sh#!/bin/bash APP_NAMEmyapp PID_FILE../logs/${APP_NAME}.pid if [ ! -f $PID_FILE ]; then echo $APP_NAME is not running exit 0 fi PID$(cat $PID_FILE) if ps -p $PID /dev/null; then echo $APP_NAME is running with PID $PID else echo $APP_NAME is not running but PID file exists rm -f $PID_FILE fi3. Windows环境脚本实现3.1 基础批处理脚本Windows下的批处理脚本需要注意路径分隔符使用反斜杠进程管理方式不同需要处理控制台编码问题start.batecho off set APP_NAMEmyapp set JAVA_OPTS-Xms512m -Xmx1024m set JAR_PATH..\lib\%APP_NAME%.jar set LOG_PATH..\logs\%APP_NAME%.log set PID_FILE..\logs\%APP_NAME%.pid :: 检查是否已运行 if exist %PID_FILE% ( for /f tokens* %%i in (%PID_FILE%) do ( tasklist /FI PID eq %%i | findstr /I %APP_NAME% if %errorlevel% equ 0 ( echo Error: %APP_NAME% is already running with PID %%i exit /b 1 ) else ( del %PID_FILE% ) ) ) :: 启动应用 start MyApp /B javaw %JAVA_OPTS% -jar %JAR% %LOG_PATH% 21 echo %errorlevel% %PID_FILE% echo %APP_NAME% started with PID %errorlevel%3.2 增强版管理脚本stop.batecho off set APP_NAMEmyapp set PID_FILE..\logs\%APP_NAME%.pid if not exist %PID_FILE% ( echo Error: PID file not found! exit /b 1 ) for /f tokens* %%i in (%PID_FILE%) do ( taskkill /PID %%i /F del %PID_FILE% echo %APP_NAME% stopped )4. 生产环境进阶技巧4.1 安全加固措施脚本权限控制chmod 750 *.sh # 禁止其他用户修改 chown appuser:appgroup *.sh # 归属应用用户敏感信息保护# 使用配置文件替代脚本中的明文密码 source ../conf/credentials.env JAVA_OPTS$JAVA_OPTS -Ddb.password$DB_PASSWORD启动前环境检查# 检查Java版本 REQUIRED_JAVA11 CURRENT_JAVA$(java -version 21 | awk -F /version/ {print $2} | cut -d. -f1) if [ $CURRENT_JAVA -lt $REQUIRED_JAVA ]; then echo Java $REQUIRED_JAVA required but found $CURRENT_JAVA exit 1 fi # 检查磁盘空间 MIN_DISK10 # GB AVAIL_DISK$(df -BG / | awk NR2 {print $4} | tr -d G) if [ $AVAIL_DISK -lt $MIN_DISK ]; then echo Insufficient disk space: ${AVAIL_DISK}G available exit 1 fi4.2 高可用方案对于关键业务系统建议增加以下机制心跳检测# 在crontab中添加定时任务 */5 * * * * /myapp/bin/healthcheck.shhealthcheck.sh#!/bin/bash APP_NAMEmyapp HEALTH_URLhttp://localhost:8080/actuator/health TIMEOUT5 # 检查进程是否存在 if ! pgrep -f $APP_NAME /dev/null; then /myapp/bin/start.sh exit 0 fi # 检查健康接口 if ! curl -sS --max-time $TIMEOUT $HEALTH_URL | grep -q status:UP; then /myapp/bin/restart.sh fi优雅停机处理# 修改stop.sh增加等待逻辑 kill $PID for i in {1..10}; do if ! ps -p $PID /dev/null; then break fi sleep 1 done if ps -p $PID /dev/null; then kill -9 $PID echo Force killed $APP_NAME fi5. 常见问题排查指南5.1 启动失败问题排查现象可能原因检查方法端口冲突已有进程占用端口netstat -tulnp | grep 8080内存不足JVM配置不合理检查-Xmx参数和系统内存依赖缺失缺少jar包或配置文件java -jar -v查看类加载日志权限不足脚本或目录不可写ls -l检查权限5.2 典型错误处理问题1PID文件残留导致无法启动# 解决方案在start.sh开头增加清理逻辑 if [ -f $PID_FILE ]; then if ! ps -p $(cat $PID_FILE) /dev/null; then rm -f $PID_FILE fi fi问题2Windows下中文乱码:: 解决方案在脚本开头设置编码 chcp 65001 nul set JAVA_TOOL_OPTIONS-Dfile.encodingUTF-8问题3Linux下文件描述符耗尽# 解决方案启动前设置ulimit ulimit -n 655355.3 性能调优建议JVM参数优化模板JAVA_OPTS-server \ -Xms4g -Xmx4g \ -XX:MetaspaceSize256m -XX:MaxMetaspaceSize256m \ -XX:UseG1GC \ -XX:MaxGCPauseMillis200 \ -XX:ParallelGCThreads4 \ -XX:ConcGCThreads2 \ -XX:HeapDumpOnOutOfMemoryError \ -XX:HeapDumpPath../logs/heapdump.hprof启动时间优化# 使用应用加速技术 JAVA_OPTS$JAVA_OPTS -XX:TieredCompilation -XX:TieredStopAtLevel1日志输出优化# 使用异步日志框架配置 JAVA_OPTS$JAVA_OPTS -DLog4jContextSelectororg.apache.logging.log4j.core.async.AsyncLoggerContextSelector
返回列表