From 53285aa17e007204fd89cded2301215a742d0c25 Mon Sep 17 00:00:00 2001 From: yumoqing Date: Sat, 16 May 2026 22:08:52 +0800 Subject: [PATCH] feat: multi-process worker scaling for Sage web app - Refactor start.sh to launch multiple worker processes based on CPU core count - Assign dynamic ports to each worker (base_port + offset) - Update stop.sh to gracefully handle and kill multiple worker PIDs - Implement PID file management for multi-process tracking --- start.sh | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ stop.sh | 79 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+) create mode 100755 start.sh create mode 100755 stop.sh diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..c18d8e0 --- /dev/null +++ b/start.sh @@ -0,0 +1,104 @@ +#!/bin/bash +# Sage Web Application Start Script +# Multi-process support based on CPU cores + +set -e + +# 切换到脚本所在目录 +cd "$(dirname "$0")" + +# 配置 +WORKDIR="$(pwd)" +PYTHON="./py3/bin/python" +APP_ENTRY="app/sage.py" +PIDFILE="$WORKDIR/sage.pid" +LOGDIR="$WORKDIR/logs" + +# 确保logs目录存在 +mkdir -p "$LOGDIR" + +# 检查Python是否存在 +if [ ! -f "$PYTHON" ]; then + echo "错误: 找不到Python解释器 $PYTHON" + echo "请确保已安装虚拟环境: ./py3/bin/python" + exit 1 +fi + +# 检查应用入口文件是否存在 +if [ ! -f "$APP_ENTRY" ]; then + echo "错误: 找不到应用入口文件 $APP_ENTRY" + exit 1 +fi + +# 检查Redis是否运行(session存储依赖) +if command -v redis-cli &> /dev/null; then + if ! redis-cli ping &> /dev/null; then + echo "Redis未运行,正在启动Redis..." + redis-server --daemonize yes + sleep 1 + if redis-cli ping &> /dev/null; then + echo "Redis启动成功" + else + echo "警告: Redis启动失败,登录功能可能无法正常工作" + fi + else + echo "Redis运行正常" + fi +fi + +echo "=========================================" +echo "启动 Sage Web Application" +echo "工作目录: $WORKDIR" +echo "Python: $PYTHON" +echo "=========================================" + +# 获取 CPU 核心数,决定启动多少 Worker +WORKERS=$(nproc) +echo "检测到 ${WORKERS} 个 CPU 核心,准备启动 ${WORKERS} 个工作进程..." + +# 获取基础端口 +BASE_PORT=9180 +if command -v python3 &> /dev/null; then + BASE_PORT=$($PYTHON -c " +import json +try: + with open('$WORKDIR/conf/config.json') as f: + config = json.load(f) + print(config.get('website', {}).get('port', 9180)) +except Exception as e: + print(9180) +" 2>/dev/null || echo 9180) +fi + +# 清空 PID 文件 +> "$PIDFILE" + +# 循环启动 Worker +for (( i=0; i>> 启动 Worker $((i+1))/${WORKERS} on port $PORT ..." + + # 启动服务 + nohup $PYTHON $APP_ENTRY --workdir "$WORKDIR" --port $PORT > "$LOGFILE" 2>&1 & + APP_PID=$! + + # 保存 PID + echo "$APP_PID" >> "$PIDFILE" + + # 短暂等待以检查是否立即崩溃 + sleep 1 + if kill -0 $APP_PID 2>/dev/null; then + echo " -> Worker PID: $APP_PID (成功)" + else + echo " -> 错误: Worker 启动失败,请查看 $LOGFILE" + fi +done + +echo "=========================================" +echo "所有服务已启动" +echo "PID 文件: $PIDFILE" +echo "访问地址: http://localhost:${BASE_PORT} (以及其他 ${WORKERS} 个端口)" +echo "=========================================" diff --git a/stop.sh b/stop.sh new file mode 100755 index 0000000..ef759ab --- /dev/null +++ b/stop.sh @@ -0,0 +1,79 @@ +#!/bin/bash +# Sage Web Application Stop Script +# Supports multi-process setup + +set -e + +# 切换到脚本所在目录 +cd "$(dirname "$0")" + +WORKDIR="$(pwd)" +PIDFILE="$WORKDIR/sage.pid" + +echo "=========================================" +echo "停止 Sage Web Application" +echo "=========================================" + +STOPPED_PIDS="" + +# 1. 尝试从 PID 文件停止 +if [ -f "$PIDFILE" ]; then + echo "读取 PID 文件..." + while read -r APP_PID; do + # 跳过空行 + if [ -z "$APP_PID" ]; then continue; fi + + if kill -0 "$APP_PID" 2>/dev/null; then + echo "正在停止 Worker (PID: $APP_PID) ..." + kill "$APP_PID" 2>/dev/null || true + STOPPED_PIDS="$STOPPED_PIDS $APP_PID" + else + echo "Worker (PID: $APP_PID) 已停止" + fi + done < "$PIDFILE" + + # 等待进程结束 + WAIT_COUNT=0 + while [ $WAIT_COUNT -lt 10 ]; do + ALL_STOPPED=true + for PID in $STOPPED_PIDS; do + if kill -0 "$PID" 2>/dev/null; then + ALL_STOPPED=false + break + fi + done + + if $ALL_STOPPED; then + break + fi + + sleep 1 + WAIT_COUNT=$((WAIT_COUNT + 1)) + echo "等待服务关闭... ($WAIT_COUNT/10)" + done + + # 强制杀死未退出的 + for PID in $STOPPED_PIDS; do + if kill -0 "$PID" 2>/dev/null; then + echo "强制停止进程: $PID" + kill -9 "$PID" 2>/dev/null || true + fi + done +fi + +# 2. 兜底清理 (通过进程名查找,防止 PID 文件丢失) +# 注意:这里匹配 app/sage.py +PIDS=$(ps aux | grep "[a]pp/sage.py" | awk '{print $2}' || true) +if [ -n "$PIDS" ]; then + echo "发现残留进程,强制清理..." + for PID in $PIDS; do + kill -9 "$PID" 2>/dev/null || true + done +fi + +# 清理 PID 文件 +rm -f "$PIDFILE" + +echo "=========================================" +echo "服务已停止" +echo "========================================="