#!/usr/bin/env bash # 每日全链路任务(由 systemd 定时器调用,也可手动执行) # # scripts/run_daily.sh # 抓取"今天" # scripts/run_daily.sh 20260924 # 指定日期 # # 重试策略:首次失败后,在 21:30、22:00 各再执行一次(可用 XWLB_RETRY_SLOTS 覆盖)。 # · 若当天分片已入库、只缺精编(切分失败)→ 重试只重跑切分,省掉全部 ASR 调用 # · 若分片都没入库(下载/识别失败) → 重试重跑全链路 # # 环境变量(一般不用改): # XWLB_RETRY_SLOTS 重试时刻,默认 "21:30 22:00" # XWLB_ATTEMPT_TIMEOUT 单次尝试上限秒数,默认 2400(40 分钟) # XWLB_ENV_FILE / XWLB_CONFIG_FILE 透传给 python 的配置覆盖 # XWLB_FORCE=1 即使当天已入库也强制重跑 # # 退出码:0=成功 1=重试用尽仍失败 2=已有实例在运行(跳过) set -uo pipefail DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$DIR" || exit 1 PY="$DIR/.venv/bin/python" LOG="$DIR/main.log" RETRY_SLOTS="${XWLB_RETRY_SLOTS:-21:30 22:00}" ATTEMPT_TIMEOUT="${XWLB_ATTEMPT_TIMEOUT:-2400}" DB_PORT="${XWLB_DB_PORT:-13306}" log() { printf '%s [run_daily] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"; } # ---- 防重入:定时器与手动执行、或两次重试叠在一起时,避免重复烧 ASR ---- exec 9>"$DIR/.run_daily.lock" if ! flock -n 9; then log "已有 run_daily 实例在运行,本次跳过" exit 2 fi # ---- 日期 ---- D8="${1:-$(date +%Y%m%d)}" D10="${D8:0:4}-${D8:4:2}-${D8:6:2}" # ---- 隧道自愈:数据库走 autossh 本地端口,隧道断了当天必失败 ---- # 先用 ss 快速判断(零成本);不通则交给 tunnel.py —— 那里是唯一的自愈实现, # 所有 Python 入口(main/getVideo5/newsProcess/...)走的是同一套逻辑。 ensure_tunnel() { ss -ltn 2>/dev/null | grep -q "127.0.0.1:${DB_PORT}" && return 0 log "数据库端口 ${DB_PORT} 不通,调用 tunnel.py 自愈(必要时执行 autossh.sh)" if "$PY" "$DIR/tunnel.py" >>"$LOG" 2>&1; then log "隧道已就绪" return 0 fi log "⚠️ 隧道仍不通(端口 ${DB_PORT}),本次很可能失败" return 1 } # 等到 HH:MM;若已过该时刻则立即返回(例如首次尝试本来就跑过了 21:30) wait_until() { local target="$1" now target_sec now=$(date +%s) target_sec=$(date -d "today ${target}" +%s 2>/dev/null) || { log "无法解析重试时刻 ${target},立即执行"; return 0; } if [ "$target_sec" -gt "$now" ]; then log "等待到 ${target} 再重试(约 $(( (target_sec - now + 59) / 60 )) 分钟)" sleep $(( target_sec - now )) else log "已过 ${target},立即重试" fi } # 失败重试时:只缺精编就走切分环节(快、便宜),否则重跑全链路 build_cmd() { local stage="$1" if [ "$stage" = "retry" ]; then "$PY" "$DIR/scripts/day_status.py" "$D10" >/dev/null 2>&1 case $? in 1) log "当天分片已入库、只缺精编 → 重试仅重跑切分环节" CMD=("$PY" "$DIR/newsProcess.py" "$D10" --force); return 0 ;; 0) log "当天数据已完整(可能上次重试已完成),无需再跑" CMD=(); return 0 ;; *) log "当天分片未入库 → 重试重跑全链路" ;; esac fi CMD=("$PY" "$DIR/getVideo5.py" "$D8" "$D8") } run_attempt() { local label="$1" stage="$2" rc build_cmd "$stage" if [ "${#CMD[@]}" -eq 0 ]; then return 0 fi log "===== ${label} 开始(${CMD[*]},上限 ${ATTEMPT_TIMEOUT}s)=====" timeout "$ATTEMPT_TIMEOUT" "${CMD[@]}" 2>&1 | tee -a "$LOG" rc=${PIPESTATUS[0]} if [ "$rc" -eq 0 ]; then log "===== ${label} 成功 =====" return 0 fi log "===== ${label} 失败(退出码 ${rc})=====" return 1 } ensure_tunnel || true # ---- 当天已完成则跳过:ext 有数据即视为完成(识别不完整的日子不会写出 ext)---- # 手动强制重跑:XWLB_FORCE=1 bash scripts/run_daily.sh <日期> if [ "${XWLB_FORCE:-0}" != "1" ]; then if "$PY" "$DIR/scripts/day_status.py" "$D10" >/dev/null 2>&1; then log "$D10 的数据已完整入库,跳过本次运行(强制重跑用 XWLB_FORCE=1 或 newsRedo.py)" exit 0 fi fi if run_attempt "首次尝试($(date +%H:%M))" full; then exit 0 fi for slot in $RETRY_SLOTS; do wait_until "$slot" if run_attempt "重试(${slot})" retry; then exit 0 fi done log "❌ 全部重试失败,${D8} 数据未完成" exit 1