软件吧
登录注册账号
/posts/task-scheduler-x41301-running
电脑必备

给计划任务加了个失败重试,结果它自己把自己跑成了死循环

极客阿凯 105 0 16

前两天给一台跑数据同步的机器写了个定时脚本,逻辑不复杂:每天凌晨拉一次远程目录,失败了就重试三次。写完顺手在计划任务里勾了「如果任务失败,按以下频率重新启动」,间隔 5 分钟,最多重试 3 次。本来以为双保险,结果第二天早上发现机器负载拉满,Task Scheduler 日志里一串 0x41301,任务状态全是 Running。

先说结论:计划任务自带的失败重启,和脚本内部的失败重试,是两个层级的东西,叠在一起就容易失控。

我当时排查的路径是这样的:

  1. 先看任务计划程序库里的「上次运行结果」,显示 0x41301,意思是任务还在运行,不是失败。这说明外层重试根本没触发,问题在脚本内部。
  2. 翻脚本日志,发现每次 rsync 失败后脚本自己 sleep 了 60 秒再重试,但网络那会儿确实断了,三次全失败后脚本 exit 1。
  3. 问题就在这儿:脚本 exit 1 之后,计划任务的「失败重启」被触发,5 分钟后又拉起一次,又是一轮三次重试。如果网络持续不通,这个循环会一直持续到下一个触发时间点,中间没有任何节流。

后来我改了三个地方:

  • 脚本内部的 sleep 重试保留,但加了指数退避(60s、180s、300s),避免短时间密集打点;
  • 计划任务里把「失败重启」关掉,改成在任务设置里加一个「如果任务运行时间超过 30 分钟则停止」,兜底防挂死;
  • 加了一个简单的锁文件(用 flock 或者 PowerShell 的 Mutex),脚本启动时先抢锁,抢不到直接退出,防止上一轮没跑完下一轮又起来。

还有个细节值得说:计划任务的「失败重启」对退出码的判断比较粗糙,只要不是 0 就算失败,包括脚本被手动 kill 的情况。如果你脚本里有 exit 2 表示「跳过」这类语义,它也会被当成失败去重启,这个坑我是后来翻文档才注意到的。

现在这台机器稳了三天,日志里没再出现叠跑。不过我还是想确认一下:你们在计划任务里做失败重试的时候,是倾向把重试逻辑全放在脚本里、任务层只做单次触发,还是两层都用、靠锁文件防重入? 我目前是前者,但总觉得锁文件在极端情况下也有窗口期,想听听有没有更干净的做法。

登录后可以为帖子点赞,但本帖内容对所有人开放阅读。

社区回应

0 Replies

还没有回应,抢一个沙发。

登录后可以回应和点赞,但本帖内容对所有人开放阅读。