5.6.5 任务可靠性、幂等与告警策略
任务可靠性、幂等与告警策略#
任务调度的核心目标是“按时执行、执行正确、可追踪、可恢复”。可靠性设计应从任务本身、调度系统、运行环境和监控告警四个维度入手,确保任务在失败时可定位、可重试、可回滚且不产生副作用。
原理草图:可靠性与告警链路#
可靠性设计要点#
- 失败可见性:统一输出日志(stdout/stderr),关联执行 ID;关键步骤记录耗时与结果。
- 可恢复性:失败后自动重试、手动重跑与中断恢复;必要时引入检查点文件。
- 资源隔离:长任务与高频任务分离;对 CPU、内存、磁盘 IO 设置限额或限流。
- 依赖显式化:任务间依赖显式声明;对上游服务做健康检查与降级。
幂等设计原则(含示例)#
- 输入幂等:以输入参数作为唯一键(日期/批次号/业务 ID)。
- 状态幂等:修改操作“查后改”;写操作使用去重表或唯一约束。
- 文件幂等:输出“临时文件 + 原子替换”;分区目录覆盖策略。
- 外部系统幂等:接口请求携带幂等键或业务唯一号。
示例:带锁与幂等文件输出
#!/usr/bin/env bash
# /opt/jobs/report_daily.sh
set -euo pipefail
JOB=report_daily
DATE="${1:-$(date +%F)}"
RUN_ID="$(date +%s)"
LOCK="/var/lock/${JOB}.lock"
OUTDIR="/data/report/${DATE}"
TMP="/data/report/${DATE}.tmp"
LOG="/var/log/${JOB}.log"
exec >>"$LOG" 2>&1
echo "[$(date)] RUN_ID=$RUN_ID DATE=$DATE"
# 幂等锁:避免并发重复执行
if ! mkdir "$LOCK" 2>/dev/null; then
echo "lock exists, exit"
exit 0
fi
trap 'rmdir "$LOCK"' EXIT
# 输入幂等:已生成则退出
if [ -f "${OUTDIR}/done.flag" ]; then
echo "already done for $DATE"
exit 0
fi
# 业务处理
mkdir -p "$TMP"
echo "report for $DATE" > "$TMP/report.txt"
# 原子替换
mkdir -p "$OUTDIR"
mv "$TMP/report.txt" "$OUTDIR/report.txt"
touch "$OUTDIR/done.flag"
rmdir "$TMP"
echo "success"
重试与补偿策略(含示例)#
- 重试规则:指数退避(1m/3m/5m/10m),区分可重试与不可重试错误。
- 最大次数:设置最大重试次数与超时上限,超过后告警并转人工处理。
- 补偿机制:失败后触发补偿任务(回滚、补发、对账)。
示例:重试包装器
#!/usr/bin/env bash
# /opt/jobs/retry_wrapper.sh
set -euo pipefail
cmd="$*"
max=4
sleep_seq=(60 180 300 600)
for i in $(seq 1 $max); do
if $cmd; then
echo "success on attempt $i"
exit 0
fi
echo "failed attempt $i"
if [ $i -lt $max ]; then
sleep "${sleep_seq[$((i-1))]}"
fi
done
echo "failed after $max attempts"
exit 1
告警策略与分级(含示例)#
- 告警分级:P1(影响业务)、P2(延迟或部分失败)、P3(性能下降)。
- 触发条件:任务失败、超时、重试超限、执行时长异常、输出为空或异常增量。
- 告警渠道:邮件/短信/IM,多通道冗余。
- 告警抑制:重试阶段仅提示,达到阈值再告警;同类告警合并与静默。
安装与命令:邮件告警(mailx)
# Debian/Ubuntu
apt-get install -y mailutils
# RHEL/CentOS
yum install -y mailx
示例:失败告警脚本
#!/usr/bin/env bash
# /opt/jobs/alert_on_fail.sh
set -euo pipefail
JOB="$1"
LOG="/var/log/${JOB}.log"
MAIL_TO="ops@example.com"
if [ ! -f "$LOG" ]; then
echo "log not found" | mail -s "[P2] $JOB log missing" "$MAIL_TO"
exit 1
fi
if tail -n 1 "$LOG" | grep -q "success"; then
exit 0
fi
tail -n 50 "$LOG" | mail -s "[P1] $JOB failed" "$MAIL_TO"
任务审计与可追踪性#
- 审计字段:任务名、调度时间、执行时间、执行节点、参数、结果、耗时、错误码。
- 追踪链路:任务与下游系统调用关联,便于故障回溯。
- 一致性检查:对账脚本或采样核验,保障批处理一致性。
示例:审计记录(CSV)
#!/usr/bin/env bash
# /opt/jobs/audit_append.sh
JOB="report_daily"
RUN_ID="$(date +%s)"
START="$(date +%F\ %T)"
HOST="$(hostname)"
PARAMS="$*"
RESULT="OK"
DURATION=12
echo "$RUN_ID,$JOB,$START,$HOST,$PARAMS,$RESULT,$DURATION" >> /var/log/job_audit.csv
systemd 定时任务可靠性配置示例#
/etc/systemd/system/report_daily.service
[Unit]
Description=Daily report job
After=network-online.target
[Service]
Type=oneshot
ExecStart=/opt/jobs/retry_wrapper.sh /opt/jobs/report_daily.sh
User=ops
Group=ops
StandardOutput=append:/var/log/report_daily.log
StandardError=append:/var/log/report_daily.log
/etc/systemd/system/report_daily.timer
[Unit]
Description=Run report daily
[Timer]
OnCalendar=*-*-* 02:00:00
Persistent=true
[Install]
WantedBy=timers.target
启用与验证
systemctl daemon-reload
systemctl enable --now report_daily.timer
systemctl list-timers | grep report_daily
journalctl -u report_daily.service -n 50
排错清单(含命令解释)#
- 任务未执行:检查定时器是否启用
systemctl list-timers(列出 timer 状态) - 脚本权限问题:检查可执行权限
ls -l /opt/jobs/report_daily.sh(确认执行位) - 锁未释放:检查锁目录
ls -ld /var/lock/report_daily.lock(确认异常残留) - 日志为空:检查重定向与输出
tail -n 50 /var/log/report_daily.log(查看最后输出) - 重复执行:检查幂等标记
ls /data/report/$(date +%F)/done.flag(是否已生成)
练习#
- 为一个每小时执行的脚本添加幂等锁与 done.flag,验证重复执行不会产生重复结果。
- 使用
retry_wrapper.sh包装一个会随机失败的脚本,观察重试与最终退出码。 - 将任务失败邮件改为 IM webhook(如企业微信/Slack),并保留邮件作为备份通道。
- 设计一个补偿脚本:当报告生成失败时,自动清理临时目录并记录审计。
- 在 systemd 定时任务中加入超时限制(
TimeoutStartSec=),并观察超时告警逻辑。