自动化:定时任务与无人值守流程

把工作流从「我点一下」变成「它自己跑」,收益是稳定的产出,代价是失败时没人立刻知道。所以自动化的顺序一定是:先手动跑顺 → 再脚本化 → 再挂定时 → 最后才谈无人值守。跳过任何一步,你得到的都只是一个安静出错的定时任务。

什么时候该上定时任务

特征适合定时不适合
触发条件时间驱动,如每天/每周固定时刻事件驱动,应该用消息或钩子
结果用途生成报告、同步数据、清理与归档需要即时判断的线上决策
失败影响可延后一次重跑,不阻塞业务失败会阻断交易链路
结果校验有明确产出可检查只有「感觉跑过了」
人工介入事后看报告即可每步都要拍板

三步走:手动 → 脚本 → 定时

先手动跑通至少三次,把每次卡住的地方记下来,再决定哪里需要人工兜底。

阶段一 手动:你在对话里让 AI 跑完整个流程,记录每一步的输入与输出;
阶段二 脚本:把已经稳定的步骤写成脚本,脚本必须自己会失败(非零退出码)、会打日志;
阶段三 定时:脚本连续手动跑一周没问题后,挂到定时任务上;
阶段四 无人值守:告警通道接通、幂等确认无误后,才允许无人值守。

一个靠谱的骨架脚本:有日志、有退出码、有失败告警。

#!/usr/bin/env bash
set -euo pipefail

TASK="weekly-doc-check"
LOG_DIR="/var/log/${TASK}"
mkdir -p "$LOG_DIR"
LOG="${LOG_DIR}/$(date +%Y%m%d-%H%M%S).log"

alert() {
  # 失败告警:把摘要发到你的告警通道,详情留在日志里
  curl -fsS -X POST "${ALERT_WEBHOOK:?未配置告警地址}" \
    -H 'Content-Type: application/json' \
    -d "{\"task\":\"${TASK}\",\"status\":\"failed\",\"step\":\"${1}\",\"log\":\"${LOG}\"}" \
    >/dev/null 2>&1 || echo "告警发送失败" >&2
}

main() {
  echo "[$(date '+%F %T')] start ${TASK}"
  ./step1_fetch.sh   || { alert step1; exit 1; }
  ./step2_process.sh || { alert step2; exit 1; }
  ./step3_report.sh  || { alert step3; exit 1; }
  echo "[$(date '+%F %T')] done"
}
main >>"$LOG" 2>&1 || { alert main; exit 1; }

挂到定时任务上,注意用绝对路径、显式设置工作目录,并注意环境变量在定时任务里不会自动继承:

# crontab -e
# 每周一 07:30 跑一次;PATH 与密钥必须在任务里显式给出
30 7 * * 1 cd /opt/tasks/weekly-doc-check && \
  PATH=/usr/local/bin:/usr/bin:/bin \
  ALERT_WEBHOOK=http://127.0.0.1:9000/hooks/task-alert \
  /bin/bash run.sh

# 系统级计划任务(宿主机层面)等价写法
# schtasks /create /tn "weekly-doc-check" /tr "D:\tasks\run.cmd" /sc weekly /d MON /st 07:30

幂等:重复执行不能把数据搞坏

定时任务最常见的故障不是「没跑」,而是「跑了两次」。幂等的意思是:同一天跑一次和跑三次,最终结果一样

手段做法适用
状态文件记录已完成的任务标记,跑过就跳过单机、小规模
加锁用锁保证同一时刻只有一个实例防止任务重叠
唯一键覆盖写入用「主键或唯一键」覆盖而非追加报表、汇总表
时间窗口过滤只处理上次成功时间之后的数据增量同步
# 用文件锁防止上一次还没跑完就叠加一次
exec 9>"/var/lock/${TASK}.lock"
flock -n 9 || { echo "上一次仍在运行,本次跳过"; exit 0; }

# 状态文件:今天已成功就不再重复处理
STATE="/var/lib/${TASK}/last-success-date"
TODAY="$(date +%F)"
[ "$(cat "$STATE" 2>/dev/null)" = "$TODAY" ] && { echo "今日已完成,跳过"; exit 0; }
# ……业务逻辑执行成功后……
echo "$TODAY" > "$STATE"

状态文件要记录「上次成功到哪一步」而不只是「今天跑没跑」,否则中途失败后重跑会漏掉或重复处理数据。

失败告警:先保证有人看得见

告警级别触发条件通道期望响应
提示任务成功但结果为空或异常偏少汇总消息次日看一眼
警告单步失败,自动重试后成功汇总消息当天关注趋势
严重连续 2 次失败、或关键产出缺失即时通知当天处理
紧急影响了对外交付或下游依赖即时通知 + 电话/值班立即处理

告警内容要能自解释,否则等于没告警:

任务名:weekly-doc-check
状态:严重,连续 2 次失败
失败步骤:step2_process.sh
错误摘要:输入目录为空,期望至少 1 个文件,实际 0 个
已尝试:重试 2 次,均在同一位置失败
日志路径:/var/log/weekly-doc-check/20250310-073001.log
建议动作:确认上游导出任务是否正常;确认目录挂载是否丢失

无人值守前,先有人值守跑一周

观察项一周内要确认的事
输入稳定性输入目录真的有数据吗?周末和节假日会不会为空?
耗时波动最长一次跑了多久?有没有和别的任务撞在同一个时间点?
失败模式失败了几次、都失败在同一步吗、是否已经修好
产物质量自动产出的报告有人看吗?有没有明显错误?
告警有效性告警发出去了吗?收到的人能看懂吗?
人工兜底任务失败后,谁在多久内能重跑一次

一周里如果有人每天都要手动补一次,说明流程还没到能无人值守的程度。

常见坑

后果规避
直接上线无人值守失败没人知道,几天后才发现先有人值守跑一周
任务无锁、耗时超过周期两个实例同时改同一份数据flock + 检查耗时
定时任务里没有 PATH 与密钥手动能跑、定时报错在任务里显式声明环境
输出只写「成功」无法判断是否真的处理了数据打印处理行数、产出文件、耗时
只告警失败,不告警「空结果」静默产出空报告空结果也算异常,按提示级告警
任务失败后无限重试把下游打崩有限重试 + 退避 + 转人工

小结:定时任务的难点从来不是 cron 的五个字段,而是幂等与告警;先手动跑顺三次、再脚本化、再挂定时,连续有人值守跑一周没出现需要人工补跑的情况,才谈无人值守——失败必须有人看得见,重跑必须不产生副作用。

笔记加载中…