上个月我想找一份三周前的备份,打开目录,空的。

可任务列表里那条备份明明显示「上次运行:成功」,时间就在两天前。
这就是定时任务最容易骗人的地方:它只回答「跑没跑」,不回答「干成了没有」。而这两件事,中间隔着好几道可以静默失败的关口。
我把手机和服务器上的任务拉成一张表,一条条对:什么时候跑、跑的是哪个脚本、往哪条通道发、最近一次成功是什么时候。二十多条对完,发现四五条在骗我。
下面这些,是按「骗得最狠」排的。
退出码是 0,不代表它干了活
那条备份是最典型的。脚本里解压、打包、上传三步,中间某一步失败过,但最后一行是 exit 0,调度器看到 0,记一笔「成功」,收工。
这类我后来统一叫「假成功」。判断标准也简单了:不看它报什么,看该有的东西在不在。备份完去看目标文件多大,推送完去解析返回的内容,少一步验证就等于没做。
回了 200,不代表消息到了
我用的两条推送通道里,有一条永远返回 200。真正的结果藏在返回体的一个字段里:0 才是成功,其余全是失败,而且失败原因写得很直白——频率超了、内容太长、key 失效。
我一开始只判断状态码,结果有两天消息根本没发出去,日志里一片绿。现在每个通道都必须解析返回内容,不为 0 就按失败处理:写日志、退非零码、让外层的检查能看见。
没东西可推的时候,它照样给你推一张空卡
有两个提醒任务,连着半个月推的是空列表。问题不在定时器,是它读的那份数据早就没人维护了,脚本读到空集合,照旧渲染、照旧发送,一切看起来都正常。
定时任务验证的是「有没有跑」,不是「有没有东西」。所以现在每个脚本都加了一条下限:抓到 0 条数据、0 张新照片、0 条记录,一律当异常报出来,宁可吵一次,也不要安安静静发张空卡。

日志是空的,你就不知道它是哪天开始坏的
对表的时候最尴尬的一屏:某个任务的日志文件 0 字节,而它每天都在正常推送。
当时的想法是「都成功了还写什么日志」。可真出事那天,你既不知道它是从哪天开始不对,也不知道上一次正常长什么样。
现在每个脚本都写一行结构化日志:时间、任务名、处理了多少条、每条通道成功与否。几行代码的事,省的是几小时翻旧账。
同一件事,别让它跑第二遍
每天推四条内容,听着简单,但重试一次就重复了——网络抖一下、手动补跑一次,同一批东西一天出现两回。
解法是让它「幂等」:状态文件里记一个「日期 + 小时」的键,跑之前先查;再留一个最近 N 条的黑名单,循环回到开头时,把刚推过的先排除掉。
只要涉及发送,就得按这个想——定时器会重试,你不能指望它只跑一次。
连报警都指望它自己发
有个晚上的任务,连着三天没执行,我一点都不知道。因为负责报警的,也是定时任务。
如果「它在跑」这件事要靠它自己证明,那这套东西就是自证的。后来单独加了一个看门狗:只做一件事,定期检查每个任务最后一次成功的时间,超时就发一条提醒。它不干活,只在别人不干活的时候叫一声。
看门狗要尽可能简单,逻辑越简单,它自己坏掉的概率越低。
手机上的定时器,不太算定时器
这条坑我最久。我在手机终端里也挂了几个任务,以为跟服务器一样可靠。实际是:切后台、系统省电、进程被清,它就不跑了,而且不会有任何报错——它只是没发生。
验证方法很土:第二天去看输出目录,有没有新文件。
所以现在手机端只留不需要准点的事:备份、归档、超期检查。真正要准时的都放服务器,手机上的任务说明里标一句「需要后台常驻,不保证触发」,不再骗自己。
最后说一句方法:先只读
对表那一步,我一个文件都没改。
先看清全貌,才知道哪几个是真问题、哪几个只是不好看。我一开始想直接动手「优化」,要是那样干,那两个空推任务大概会被当成「配置问题」改坏。
二十多个任务,真正咬人的就那几种:假成功、假送达、空数据、没日志、重复跑、连报警一起死,以及把可靠性押在一个随时会被系统杀掉的进程上。
补完之后,我对它们的信任方式终于变了:从「它每天早上都响」,变成「它不响的时候,会有人告诉我」。