首次提交
This commit is contained in:
164
scripts/启动.sh
Normal file
164
scripts/启动.sh
Normal file
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# 一键启动(防 SIGHUP/外部kill 脱离终端 + 调好参数 + 启动前自检 + 防重复启动)
|
||||
#
|
||||
# 两种模式(MODE 环境变量切换):
|
||||
# MODE=worker (默认) —— 常驻 worker, 从 COS 自动认领任务(run_worker.py)。不需要本地 text2。
|
||||
# MODE=local —— 旧模式, 处理本地 ./text2 下的任务txt(main.py)。
|
||||
#
|
||||
# 用法:
|
||||
# bash scripts/启动.sh # 默认 worker 模式启动
|
||||
# bash scripts/启动.sh 32 # 临时把 max-workers 改成 32
|
||||
# MODE=local bash scripts/启动.sh # 用旧的本地 text2 模式
|
||||
# MAX_WORKERS=20 MEM_GUARD=40 bash scripts/启动.sh
|
||||
#
|
||||
# 停止: bash scripts/stop.sh -y
|
||||
# =============================================================================
|
||||
set -u
|
||||
cd "$(cd "$(dirname "$0")/.." && pwd)"
|
||||
ROOT="$(pwd)"
|
||||
|
||||
# ---- 可调参数(命令行第1个参数 / 环境变量 均可覆盖) ----
|
||||
# MODE 未显式指定时, 读 config.dispatch.source 决定: local -> 本地text2, 其余 -> COS常驻
|
||||
if [ -z "${MODE:-}" ]; then
|
||||
_src="$(python -c "from config.config_utils import config; print((config.get_dispatch_config() or {}).get('source','cos'))" 2>/dev/null || echo cos)"
|
||||
[ "$_src" = "local" ] && MODE="local" || MODE="worker"
|
||||
fi
|
||||
MODE="${MODE:-worker}" # worker=COS认领常驻 / local=本地text2
|
||||
MAX_WORKERS="${1:-${MAX_WORKERS:-25}}" # 0=不传给python,由main.py/run_worker.py自动按本机CPU核数选
|
||||
# 共享机/跟OCR服务同机部署的, 建议显式传小一点的值(如24), 别用自动
|
||||
MAX_TASKS="${MAX_TASKS:-0}" # 0=不回收worker(避免3.11回收缩水把并发缩到0)
|
||||
MEM_GUARD="${MEM_GUARD:-30}" # 可用内存低于此(G)暂停启动新视频
|
||||
SOURCE_LIST="${SOURCE_LIST:-./text2}" # 仅 local 模式: 任务txt目录
|
||||
EXECUTOR="${EXECUTOR:-process}" # process=进程池(绕GIL) / thread
|
||||
LOG="${LOG:-app.log}"
|
||||
NICE="${NICE:-15}" # 处理进程CPU优先级(0~19,越大越低)
|
||||
LOG_MAX_MB="${LOG_MAX_MB:-200}" # $LOG 超过这个大小(MB)就在启动前轮转一次, 0=不轮转
|
||||
|
||||
# ---- 启动前轮转一次日志(常驻进程一跑数天/数周, >>追加写不会自己变小) ----
|
||||
_rotate_log() {
|
||||
local f="$1" maxmb="$2"
|
||||
[ "$maxmb" -le 0 ] && return 0
|
||||
[ -f "$f" ] || return 0
|
||||
local sz_mb=$(( $(stat -c%s "$f" 2>/dev/null || echo 0) / 1024 / 1024 ))
|
||||
if [ "$sz_mb" -ge "$maxmb" ]; then
|
||||
local ts; ts="$(date +%Y%m%d_%H%M%S)"
|
||||
mv "$f" "${f}.${ts}" 2>/dev/null && echo "[日志轮转] $f (${sz_mb}M) → ${f}.${ts}, 重新开始写"
|
||||
ls -t "${f}".[0-9]* 2>/dev/null | tail -n +6 | xargs -r rm -f # 只留最近5份轮转备份
|
||||
fi
|
||||
}
|
||||
_rotate_log "$LOG" "$LOG_MAX_MB"
|
||||
|
||||
# 按模式确定入口与进程匹配模式
|
||||
if [ "$MODE" = "worker" ]; then
|
||||
ENTRY="run_worker.py"
|
||||
PROC_PAT='python .*run_worker\.py'
|
||||
else
|
||||
ENTRY="main.py"
|
||||
PROC_PAT='python .*main\.py'
|
||||
fi
|
||||
|
||||
MAX_WORKERS_DISP="$MAX_WORKERS"
|
||||
[ "$MAX_WORKERS" = "0" ] && MAX_WORKERS_DISP="0(自动按CPU核数选, 实际值见下面python启动日志)"
|
||||
|
||||
echo "=================================================="
|
||||
echo " 启动 $ENTRY (MODE=$MODE)"
|
||||
echo " max-workers=$MAX_WORKERS_DISP max-tasks-per-child=$MAX_TASKS mem-guard=${MEM_GUARD}G"
|
||||
echo " executor=$EXECUTOR nice=$NICE(让OCR服务优先) 日志→$LOG"
|
||||
[ "$MODE" = "local" ] && echo " source-list=$SOURCE_LIST"
|
||||
echo "=================================================="
|
||||
|
||||
# ---- 1. 防重复启动 ----
|
||||
if pgrep -af "$PROC_PAT" 2>/dev/null | grep -v 'multiprocessing' | grep -v 'grep' >/dev/null; then
|
||||
echo "✗ 已经有 $ENTRY 在运行了, 不重复启动。"
|
||||
echo " 查看: pgrep -af '$PROC_PAT' | grep -v multiprocessing"
|
||||
echo " 如要重启: 先 bash scripts/stop.sh -y"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ---- 2. 启动前自检 ----
|
||||
fail=0
|
||||
need() { command -v "$1" >/dev/null 2>&1 && echo " ✓ $1" || { echo " ✗ 缺少 $1 (需在 PATH 或项目目录)"; fail=1; }; }
|
||||
echo "[依赖检查]"
|
||||
need python
|
||||
need ffmpeg
|
||||
need ffprobe
|
||||
if command -v coscli >/dev/null 2>&1; then echo " ✓ coscli"
|
||||
elif [ -x "./coscli-linux-amd64" ]; then echo " ✓ ./coscli-linux-amd64"
|
||||
else echo " ✗ 找不到 coscli / ./coscli-linux-amd64"; fail=1; fi
|
||||
|
||||
echo "[配置/任务检查]"
|
||||
[ -f config/config.json ] && echo " ✓ config/config.json" || { echo " ✗ 缺 config/config.json"; fail=1; }
|
||||
|
||||
if [ "$MODE" = "worker" ]; then
|
||||
# worker 模式: 任务来自 COS, 不需要本地 text2; 检查 dispatch 配置与机器ID + COS 队列可达
|
||||
chk=$(python - <<'PY' 2>&1
|
||||
import sys
|
||||
try:
|
||||
from config.config_utils import config
|
||||
from tool import job_cos
|
||||
dp = config.get_dispatch_config() or {}
|
||||
if not dp.get("jobs_root"):
|
||||
print("NO_JOBS_ROOT"); sys.exit(0)
|
||||
mid = job_cos.get_machine_id()
|
||||
todo = job_cos.list_todo() # 顺带验证 COS 队列可达
|
||||
print("OK\t%s\t%d" % (mid, len(todo)))
|
||||
except Exception as e:
|
||||
print("ERR\t%s" % e)
|
||||
PY
|
||||
)
|
||||
case "$chk" in
|
||||
OK*) mid=$(echo "$chk" | cut -f2); n=$(echo "$chk" | cut -f3)
|
||||
echo " ✓ dispatch 配置OK 机器ID=$mid todo剩余分片=$n"
|
||||
[ "$n" = "0" ] && echo " ⚠ todo 队列为空(worker会空闲等待; 先 python tools/seed_jobs.py 灌任务)";;
|
||||
NO_JOBS_ROOT) echo " ✗ config.dispatch.jobs_root 未配置(取任务地址)"; fail=1;;
|
||||
*) echo " ✗ 访问 COS 任务队列失败: ${chk#ERR }"; fail=1;;
|
||||
esac
|
||||
else
|
||||
if ls "$SOURCE_LIST"/*.txt >/dev/null 2>&1; then
|
||||
n=$(ls "$SOURCE_LIST"/*.txt 2>/dev/null | wc -l)
|
||||
echo " ✓ $SOURCE_LIST 有 $n 个任务txt"
|
||||
else
|
||||
echo " ✗ $SOURCE_LIST 下没有 .txt 任务文件"; fail=1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$fail" -ne 0 ]; then
|
||||
echo ""
|
||||
echo "✗ 自检未通过, 已中止(没启动)。修好上面 ✗ 的项再跑。"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# ---- 3. 启动(setsid+nohup+disown 三保险, SSH断开也杀不到) ----
|
||||
echo ""
|
||||
echo "[启动中...]"
|
||||
if [ "$MODE" = "worker" ]; then
|
||||
setsid nohup nice -n "$NICE" python run_worker.py \
|
||||
--max-workers "$MAX_WORKERS" \
|
||||
--max-tasks-per-child "$MAX_TASKS" \
|
||||
--mem-guard-gb "$MEM_GUARD" \
|
||||
--executor "$EXECUTOR" >> "$LOG" 2>&1 &
|
||||
else
|
||||
setsid nohup nice -n "$NICE" python main.py \
|
||||
--source-list "$SOURCE_LIST" \
|
||||
--max-workers "$MAX_WORKERS" \
|
||||
--max-tasks-per-child "$MAX_TASKS" \
|
||||
--mem-guard-gb "$MEM_GUARD" \
|
||||
--executor "$EXECUTOR" >> "$LOG" 2>&1 &
|
||||
fi
|
||||
disown 2>/dev/null || true
|
||||
|
||||
# ---- 4. 等几秒确认起来了 ----
|
||||
sleep 6
|
||||
if pgrep -af "$PROC_PAT" 2>/dev/null | grep -v 'multiprocessing' | grep -v 'grep' >/dev/null; then
|
||||
echo "✓ 已启动。日志末尾:"
|
||||
tail -n 5 "$LOG" 2>/dev/null | sed 's/^/ /'
|
||||
echo ""
|
||||
echo " 实时看日志: tail -f $LOG"
|
||||
echo " 数worker: pgrep -af multiprocessing | wc -l (稍等会接近 $MAX_WORKERS_DISP)"
|
||||
echo " 停止: bash scripts/stop.sh -y"
|
||||
else
|
||||
echo "✗ 启动后没检测到进程, 查日志看原因:"
|
||||
tail -n 20 "$LOG" 2>/dev/null | sed 's/^/ /'
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user