服务启动停止脚本?

wen 实用脚本 45

本文目录导读:

服务启动停止脚本?

  1. 通用 Shell 脚本 (适用于 Java、Python 等后台进程)
  2. 针对 Java 程序 (Systemd 风格 - 推荐生产环境)
  3. 针对 Python 程序 (使用 Gunicorn/Uvicorn)
  4. 针对 Go / C / C++ 编译的二进制程序
  5. Windows 批处理脚本 .bat
  6. 总结最佳实践

这里为您整理了几种常见环境下(Java、Python、通用服务)的启动停止脚本示例,并加入了最佳实践(如PID文件、检测进程、优雅关闭)。


通用 Shell 脚本 (适用于 Java、Python 等后台进程)

这是最经典的模板,使用 PID 文件来管理进程。

#!/bin/bash
# description: 通用服务启动/停止脚本
# usage: ./service.sh {start|stop|restart|status}
# 请修改以下变量为您的服务名和启动命令
APP_NAME="my_app"
APP_HOME="/path/to/your/app"
EXECUTABLE="java -jar $APP_HOME/app.jar"
PID_FILE="$APP_HOME/$APP_NAME.pid"
LOG_FILE="$APP_HOME/$APP_NAME.log"
# 获取当前进程 PID
get_pid() {
    if [ -f "$PID_FILE" ]; then
        cat "$PID_FILE"
    else
        echo ""
    fi
}
# 检查进程是否在运行
is_running() {
    local pid=$(get_pid)
    if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
        return 0  # true
    else
        return 1  # false
    fi
}
# 启动服务
start() {
    if is_running; then
        echo "$APP_NAME 已经在运行中 (PID: $(get_pid))"
        exit 1
    fi
    echo "正在启动 $APP_NAME ..."
    # 使用 nohup 在后台运行,并输出日志
    nohup $EXECUTABLE > "$LOG_FILE" 2>&1 &
    local new_pid=$!
    echo $new_pid > "$PID_FILE"
    sleep 1
    if is_running; then
        echo "$APP_NAME 启动成功 (PID: $new_pid)"
    else
        echo "$APP_NAME 启动失败,请检查日志: $LOG_FILE"
        exit 1
    fi
}
# 停止服务
stop() {
    if ! is_running; then
        echo "$APP_NAME 未在运行"
        return 0
    fi
    local pid=$(get_pid)
    echo "正在停止 $APP_NAME (PID: $pid) ..."
    # 1. 尝试优雅关闭 (SIGTERM)
    kill "$pid" 2>/dev/null
    # 2. 等待进程结束(最多等待 30 秒)
    local wait_count=0
    while is_running && [ $wait_count -lt 30 ]; do
        sleep 1
        ((wait_count++))
    done
    # 3. 如果还在运行,强制杀掉 (SIGKILL)
    if is_running; then
        echo "进程未能优雅停止,强制执行 kill -9 ..."
        kill -9 "$pid" 2>/dev/null
        sleep 1
    fi
    # 4. 删除 PID 文件
    rm -f "$PID_FILE"
    echo "$APP_NAME 已停止"
}
# 重启服务
restart() {
    stop
    sleep 2  # 等待端口释放
    start
}
# 查看状态
status() {
    if is_running; then
        echo "$APP_NAME 正在运行 (PID: $(get_pid))"
    else
        echo "$APP_NAME 未在运行"
    fi
}
# 主入口
case "$1" in
    start)
        start
        ;;
    stop)
        stop
        ;;
    restart)
        restart
        ;;
    status)
        status
        ;;
    *)
        echo "用法: $0 {start|stop|restart|status}"
        exit 1
        ;;
esac

针对 Java 程序 (Systemd 风格 - 推荐生产环境)

对于 CentOS 7+ / Ubuntu 16+,使用 systemd 是最现代化的方式。

文件路径: /etc/systemd/system/myapp.service

[Unit]
Description=My Java Application Service
After=network.target
[Service]
Type=simple
User=appuser
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/java -Xms256m -Xmx1024m -jar /opt/myapp/app.jar
ExecStop=/bin/kill -s TERM $MAINPID
SuccessExitStatus=143
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
[Install]
WantedBy=multi-user.target

使用方法:

# 重新加载配置
sudo systemctl daemon-reload
# 启动
sudo systemctl start myapp
# 停止
sudo systemctl stop myapp
# 重启
sudo systemctl restart myapp
# 查看状态
sudo systemctl status myapp
# 设置为开机自启
sudo systemctl enable myapp

针对 Python 程序 (使用 Gunicorn/Uvicorn)

对于 Web 应用 (Flask/Django/FastAPI) 经常配合 Gunicorn。

文件路径: /etc/init.d/my-python-app 或作为 systemd 服务。

#!/bin/bash
# chkconfig: 2345 80 20
# description: Python Web Service
APP_NAME="webapp"
APP_PATH="/opt/myapp"
VENV_PATH="$APP_PATH/venv/bin/activate"
GUNICORN="$APP_PATH/venv/bin/gunicorn"
PID_FILE="/var/run/$APP_NAME.pid"
start() {
    echo "Starting $APP_NAME ..."
    cd $APP_PATH
    source $VENV_PATH
    $GUNICORN --workers 3 --bind 0.0.0.0:8000 wsgi:app \
              --pid $PID_FILE \
              --daemon \
              --access-logfile /var/log/$APP_NAME/access.log \
              --error-logfile /var/log/$APP_NAME/error.log
    echo "$APP_NAME started."
}
stop() {
    echo "Stopping $APP_NAME ..."
    if [ -f $PID_FILE ]; then
        kill -TERM $(cat $PID_FILE) 2>/dev/null
        rm -f $PID_FILE
    fi
    echo "$APP_NAME stopped."
}
case "$1" in
    start) start ;;
    stop) stop ;;
    restart) stop; sleep 2; start ;;
    *) echo "Usage: $0 {start|stop|restart}" ;;
esac

针对 Go / C / C++ 编译的二进制程序

Go 或 C 程序通常是单文件二进制,启动简单。

#!/bin/bash
APP="/usr/local/bin/my-server"
APP_NAME="myserver"
PID_FILE="/var/run/${APP_NAME}.pid"
start() {
    if [ -f $PID_FILE ] && kill -0 $(cat $PID_FILE) 2>/dev/null; then
        echo "$APP_NAME already running."
        exit 1
    fi
    echo "Starting $APP_NAME..."
    $APP &>/dev/null &
    echo $! > $PID_FILE
    echo "$APP_NAME started."
}
stop() {
    if [ ! -f $PID_FILE ]; then
        echo "$APP_NAME not running."
        exit 1
    fi
    echo "Stopping $APP_NAME..."
    kill $(cat $PID_FILE) 2>/dev/null
    rm -f $PID_FILE
    echo "$APP_NAME stopped."
}
case "$1" in
    start|stop|restart) $1 ;;
    *) echo "Usage: $0 {start|stop|restart}" ;;
esac

Windows 批处理脚本 .bat

@echo off
set APP_NAME=MyApp
set JAR_PATH=C:\myapp\app.jar
set PID_FILE=%TEMP%\%APP_NAME%.pid
if "%1"=="start" goto start
if "%1"=="stop" goto stop
if "%1"=="restart" goto restart
echo Usage: %0 {start^|stop^|restart}
goto :eof
:start
echo Starting %APP_NAME% ...
start "%~n0" javaw -jar %JAR_PATH%
echo %ERRORLEVEL% > %PID_FILE%
echo %APP_NAME% started.
goto :eof
:stop
echo Stopping %APP_NAME% ...
taskkill /f /fi "PID eq %PID_FILE%" 2>nul
del %PID_FILE% 2>nul
echo %APP_NAME% stopped.
goto :eof
:restart
call :stop
timeout /t 2 /nobreak >nul
call :start
goto :eof

总结最佳实践

特性 说明
PID 文件 用于存储进程ID,方便查找和杀死
SIGTERM vs SIGKILL 先发 TERM (15) 优雅关闭,等几秒再发 KILL (9) 强制
日志重定向 nohup + 2>&1 将输出写入文件
状态检查 kill -0 PID 检查进程是否存在
Systemd 生产环境推荐,自带PID、自动重启、资源限制

您可以根据自己的服务类型选择对应的脚本模板,如果您有具体的服务(比如是 Spring Boot、Flask、Node.js),我可以帮您定制更精确的版本。

抱歉,评论功能暂时关闭!