1. 为什么需要专业的Java应用启停脚本在Linux和Windows服务器上部署Java应用时很多开发者习惯直接使用java -jar命令启动应用然后用CtrlC或关闭终端的方式停止。这种简单粗暴的方式在实际生产环境中会带来诸多问题进程意外终止SSH连接断开会导致Linux上的Java进程被终止日志丢失控制台输出没有重定向关键日志无法追溯资源泄漏Windows上直接关闭CMD窗口可能导致资源未释放管理困难没有标准化的管理接口批量操作效率低下我经历过一个典型的生产事故某次服务器维护时运维人员直接kill了Java进程导致文件锁未被释放重启后应用无法正常写入数据。这就是缺乏规范启停流程的代价。2. Linux环境下的Java应用启停方案2.1 基础Shell脚本实现最基本的启动脚本可以这样编写保存为start.sh#!/bin/bash APP_NAMEmyapp JAVA_OPTS-Xms512m -Xmx1024m -Dspring.profiles.activeprod JAR_FILEtarget/myapp.jar LOG_FILElogs/app.log nohup java $JAVA_OPTS -jar $JAR_FILE $LOG_FILE 21 echo $! pid.file关键点解析nohup保证SSH断开后进程继续运行21将标准错误重定向到标准输出echo $!获取并保存进程ID到文件对应的停止脚本stop.sh#!/bin/bash if [ -f pid.file ]; then kill $(cat pid.file) rm -f pid.file else echo PID file not found fi2.2 进阶Systemd服务配置对于需要开机自启的服务推荐使用Systemd。创建/etc/systemd/system/myapp.service[Unit] DescriptionMy Java Application Aftersyslog.target network.target [Service] Userappuser WorkingDirectory/opt/myapp ExecStart/usr/bin/java -Xms512m -Xmx1024m -jar myapp.jar SuccessExitStatus143 Restartalways RestartSec30 [Install] WantedBymulti-user.target使用命令管理服务systemctl daemon-reload systemctl start myapp systemctl enable myapp # 开机自启2.3 生产环境增强方案在实际运维中我们还需要考虑启动超时控制timeout 60s java -jar app.jar || { echo 启动超时 exit 1 }健康检查机制# 启动后检查端口是否监听 if ! nc -z localhost 8080; then echo 应用未正常启动 exit 1 fi日志轮转配置logrotate/opt/myapp/logs/*.log { daily rotate 30 compress missingok notifempty copytruncate }3. Windows环境下的批处理方案3.1 基础批处理脚本启动脚本start.batecho off set JAVA_OPTS-Xms512m -Xmx1024m set JAR_FILEmyapp.jar set LOG_FILElogs\app.log start MyApp javaw %JAVA_OPTS% -jar %JAR_FILE% %LOG_FILE% 21 echo %errorlevel% pid.file停止脚本stop.batecho off for /f tokens1 %%i in (type pid.file) do ( taskkill /PID %%i /F ) del pid.file3.2 Windows服务化部署使用NSSM工具将Java应用安装为Windows服务下载nssm.exe安装服务nssm install MyJavaApp %JAVA_HOME%\bin\javaw.exe -Xms512m -Xmx1024m -jar myapp.jar nssm start MyJavaApp3.3 常见Windows问题解决问题1控制台窗口闪退使用javaw代替java可避免控制台窗口或者使用start /B在后台运行问题2端口占用for /f tokens5 %%a in (netstat -ano ^| findstr 8080) do ( taskkill /PID %%a /F )问题3环境变量失效在脚本开头显式设置JAVA_HOMEset JAVA_HOMEC:\Program Files\Java\jdk1.8.0_291 set PATH%JAVA_HOME%\bin;%PATH%4. 跨平台通用方案与最佳实践4.1 启动参数优化建议根据应用类型调整JVM参数Web应用-XX:UseG1GC -XX:MaxGCPauseMillis200批处理应用-XX:UseParallelGC -XX:ParallelGCThreads4内存计算经验公式Xmx 可用物理内存 * 0.75 - 其他服务占用内存4.2 状态检查与监控通用的健康检查脚本#!/bin/bash PID$(cat pid.file 2/dev/null) || exit 1 if ps -p $PID /dev/null; then # 检查端口 if nc -z localhost 8080; then # 检查HTTP接口 if curl -s http://localhost:8080/health | grep -q status:UP; then exit 0 fi fi fi exit 14.3 安全加固措施禁止root运行if [ $(id -u) 0 ]; then echo 请勿使用root用户运行 2 exit 1 fi文件权限控制chown -R appuser:appgroup /opt/myapp chmod 750 /opt/myapp/bin/*.sh敏感信息处理# 使用环境变量而非明文密码 export DB_PASSWORD$(aws secretsmanager get-secret-value --secret-id db-creds --query SecretString --output text) java -jar app.jar5. 实际案例Spring Boot应用部署5.1 带Profile控制的启动脚本#!/bin/bash ACTIVE_PROFILE${1:-dev} case $ACTIVE_PROFILE in prod) JAVA_OPTS-Xms2g -Xmx2g -XX:UseG1GC ;; staging) JAVA_OPTS-Xms1g -Xmx1g ;; *) JAVA_OPTS-Xms512m -Xmx512m ;; esac nohup java $JAVA_OPTS -Dspring.profiles.active$ACTIVE_PROFILE -jar app.jar /dev/null 21 5.2 优雅停机实现对于Spring Boot应用发送SIGTERM信号触发优雅停机#!/bin/bash PID$(cat pid.file 2/dev/null) || { echo PID文件不存在; exit 1; } kill -15 $PID TIMEOUT30 while kill -0 $PID 2/dev/null [ $TIMEOUT -gt 0 ]; do sleep 1 TIMEOUT$((TIMEOUT-1)) done if [ $TIMEOUT -eq 0 ]; then kill -9 $PID echo 强制终止进程 fi5.3 启动前置检查#!/bin/bash # 检查Java版本 REQUIRED_JAVA11 JAVA_VERSION$(java -version 21 | awk -F /version/ {print $2}) if [[ $JAVA_VERSION $REQUIRED_JAVA ]]; then echo 需要Java $REQUIRED_JAVA或更高版本 exit 1 fi # 检查端口占用 if lsof -i :8080 /dev/null; then echo 8080端口已被占用 exit 1 fi # 检查磁盘空间 MIN_DISK10 # GB AVAILABLE$(df -BG / | awk NR2 {print $4} | tr -d G) if [ $AVAILABLE -lt $MIN_DISK ]; then echo 磁盘空间不足需要至少${MIN_DISK}GB exit 1 fi6. 容器化部署的特别考量6.1 Docker环境适配当应用运行在Docker容器中时启停脚本需要特殊处理信号处理#!/bin/sh # 使用exec保证Java进程接收信号 exec java -jar app.jar健康检查HEALTHCHECK --interval30s --timeout3s \ CMD curl -f http://localhost:8080/actuator/health || exit 1优雅停机STOPSIGNAL SIGTERM6.2 Kubernetes部署模式在K8s中通过以下配置实现优雅启停spec: containers: - name: myapp lifecycle: preStop: exec: command: [sh, -c, sleep 30] # 等待流量切走 livenessProbe: httpGet: path: /actuator/health port: 8080 readinessProbe: httpGet: path: /actuator/health port: 80807. 常见问题排查指南7.1 启动失败排查步骤检查Java版本java -version查看日志尾部tail -n 50 logs/app.log手动运行验证java -jar app.jar检查端口占用netstat -tulnp | grep 80807.2 内存溢出处理当出现OutOfMemoryError时添加JVM参数收集dump-XX:HeapDumpOnOutOfMemoryError -XX:HeapDumpPath/tmp分析工具jmap -heap pid # 查看堆内存 jstat -gcutil pid # GC统计7.3 线程死锁诊断获取线程dumpjstack -l pid thread.dump查找关键字deadlock BLOCKED8. 自动化部署集成8.1 与CI/CD流水线结合在Jenkins pipeline中的典型用法pipeline { stages { stage(Deploy) { steps { sh chmod x scripts/*.sh scripts/stop.sh || true scripts/start.sh prod } } } }8.2 配置管理工具集成Ansible playbook示例- name: Deploy Java app hosts: app_servers tasks: - name: Copy application files copy: src: {{ app_dir }} dest: /opt/myapp - name: Ensure scripts are executable file: path: /opt/myapp/scripts/{{ item }} mode: 0755 loop: - start.sh - stop.sh - name: Restart application command: /opt/myapp/scripts/restart.sh9. 性能优化技巧9.1 JVM参数调优根据应用特点调整GC策略低延迟应用-XX:UseZGC -Xmx4g -Xms4g高吞吐量应用-XX:UseParallelGC -XX:ParallelGCThreads89.2 启动速度优化使用应用类数据共享-XX:UseAppCDS -Xshare:on模块化部署Java 9--module-path mods -m com.myapp/com.myapp.Main9.3 资源限制配置在cgroups环境中如Docker-XX:UseContainerSupport -XX:MaxRAMPercentage70.010. 版本升级与回滚10.1 蓝绿部署脚本#!/bin/bash # 当前运行版本 CURRENT_VERSION$(readlink /opt/myapp/current) # 部署新版本 NEW_VERSIONmyapp-1.1.0 unzip -q /tmp/${NEW_VERSION}.zip -d /opt/myapp/versions/ ln -sfn /opt/myapp/versions/${NEW_VERSION} /opt/myapp/new # 切换流量 /opt/myapp/new/bin/start.sh sleep 30 # 等待新版本预热 ln -sfn /opt/myapp/new /opt/myapp/current # 停止旧版本 if [ -n $CURRENT_VERSION ]; then /opt/myapp/$CURRENT_VERSION/bin/stop.sh fi10.2 回滚机制#!/bin/bash PREV_VERSION$(ls -t /opt/myapp/versions | head -2 | tail -1) ln -sfn /opt/myapp/versions/$PREV_VERSION /opt/myapp/current /opt/myapp/current/bin/restart.sh