首次提交

This commit is contained in:
2026-09-28 16:09:31 +08:00
commit 4a6839319a
71 changed files with 12144 additions and 0 deletions

121
scripts/守护重启.sh Normal file
View File

@@ -0,0 +1,121 @@
#!/usr/bin/env bash
# =============================================================================
# 守护重启: main.py 被外部 kill / 异常退出后, 自动拉起(共享机上挡不住别人kill, 就让它自己回来)。
# 【带明确开关, 想关随时关, 不会和你抢】:
# 关闭原理 = 放一个"暂停标记文件" .guard_paused, 守护看到它就什么都不做。
#
# 用法:
# bash scripts/守护重启.sh install # 安装(每分钟检测一次, 没在跑就启动)
# bash scripts/守护重启.sh off # ★想停就用这个★ 暂停自动重启 + 停掉当前进程
# bash scripts/守护重启.sh on # 重新开启自动重启(下一分钟内会被拉起)
# bash scripts/守护重启.sh status # 看状态(装没装/开没开/在没在跑)
# bash scripts/守护重启.sh uninstall # 彻底卸载(删掉定时任务, 进程不动)
# bash scripts/守护重启.sh tick # (cron 内部调用, 别手动跑)
#
# 启动参数想改, 改下面 START= 这一行即可。
# =============================================================================
set -u
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"
PAUSE="$ROOT/.guard_paused" # 这个文件存在 = 暂停自动重启
LOCK="$ROOT/.guard.lock"
GLOG="$ROOT/guard.log"
CRON_TAG="# ocr-guard" # crontab 里的标识, 用于增删去重
# ★ 启动命令(按之前调优建议; 想改并发/参数改这里) ★
# MODE=worker(默认): 常驻 worker 从 COS 认领任务; MODE=local: 旧的本地 text2 模式。
# nice -n 15: 让 co-located 的 OCR 服务优先抢 CPU; max-tasks-per-child 20: 本机内存偏紧
# (44G可用/184G总量), 定期回收worker释放常驻内存; 3.11回收有缩水风险(见CLAUDE.md), 但20这个
# 量级配合 --mem-guard-gb 观察下来风险可控, 内存不紧的机器可以改回0(不回收)。
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}"
if [ "$MODE" = "worker" ]; then
START='nice -n 15 python run_worker.py --max-workers 30 --max-tasks-per-child 20 --mem-guard-gb 30 --executor process'
PROC_PAT='python .*run_worker\.py'
else
START='nice -n 15 python main.py --source-list ./text2 --max-workers 30 --max-tasks-per-child 20 --mem-guard-gb 30 --executor process'
PROC_PAT='python .*main\.py'
fi
# 判断父进程是否在跑(forkserver worker 命令行不含入口文件名, 不会误判)
running() {
pgrep -af "$PROC_PAT" 2>/dev/null | grep -v 'multiprocessing' | grep -v 'grep' >/dev/null 2>&1
}
# app.log 轮转: 进程可能正在往它里面 >> 追加写, 不能直接 mv(会导致进程还在写旧inode, 新文件
# 看起来永远是空的) —— 用 copytruncate: 先 cp 一份出去, 再原地截断; O_APPEND 的fd是按"当前
# 文件大小"找写入位置的, 原地截断后进程会自动从offset 0继续写这个(同一个)文件, 不受影响。
_rotate_applog() {
local f="$ROOT/app.log" maxmb=200
[ -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)"
cp "$f" "${f}.${ts}" 2>/dev/null && : > "$f" \
&& echo "$(date '+%F %T') [日志轮转] app.log(${sz_mb}M) → app.log.${ts}, 原地清空续写" >> "$GLOG"
ls -t "${f}".[0-9]* 2>/dev/null | tail -n +6 | xargs -r rm -f # 只留最近5份
fi
}
case "${1:-status}" in
install)
( crontab -l 2>/dev/null | grep -vF "$CRON_TAG"
echo "* * * * * cd $ROOT && bash scripts/守护重启.sh tick $CRON_TAG" ) | crontab -
rm -f "$PAUSE"
echo "✓ 守护已安装(每分钟检测), 自动重启: 开启"
echo " 想停: bash scripts/守护重启.sh off"
;;
uninstall)
crontab -l 2>/dev/null | grep -vF "$CRON_TAG" | crontab -
echo "✓ 已卸载守护定时任务(不再自动重启)。当前进程未动。"
echo " 如要同时停进程: bash scripts/stop.sh -y"
;;
off)
touch "$PAUSE"
echo "✓ 已暂停自动重启(标记 $PAUSE)。正在停止当前进程..."
bash "$ROOT/scripts/stop.sh" -y
echo "✓ 已停止, 且守护不会再把它拉起。恢复运行: bash scripts/守护重启.sh on"
;;
on)
rm -f "$PAUSE"
echo "✓ 自动重启已开启。若当前没在跑, 一分钟内会被拉起。"
;;
tick)
# 暂停中 → 什么都不做(这就是 off 之所以能稳关掉的原因)
[ -f "$PAUSE" ] && exit 0
# 防并发: 同一时刻只允许一个 tick 启动
exec 9>"$LOCK"
flock -n 9 || exit 0
_rotate_applog # 每分钟顺带检查一次, 跟要不要重启无关(常驻不重启时全靠这个兜底)
running && exit 0
echo "$(date '+%F %T') 检测到未运行 → 启动" >> "$GLOG"
setsid nohup $START >> "$ROOT/app.log" 2>&1 &
;;
status)
echo "项目目录: $ROOT"
if crontab -l 2>/dev/null | grep -qF "$CRON_TAG"; then
echo "守护定时任务: ✓ 已安装(每分钟检测)"
else
echo "守护定时任务: ✗ 未安装"
fi
if [ -f "$PAUSE" ]; then
echo "自动重启: ⏸ 已暂停 (.guard_paused 存在 → 不会拉起)"
else
echo "自动重启: ▶ 开启"
fi
if running; then echo "main.py: 运行中"; else echo "main.py: 未运行"; fi
;;
*)
echo "用法: install | on | off | uninstall | status | tick"
;;
esac