定时任务要能安全重跑:幂等性设计的四个落点

举报
yd_213533429 发表于 2026/10/30 08:57:32 2026/10/30
【摘要】 我每天清晨 6:00 让一个浏览器自动化任务替我做几件重复的事:登录、签到、把当天能做的积分任务做掉、最后回读积分明细写一份汇报。这套东西跑了十来天,踩得最多的坑不是「能不能做对」,而是「重跑会不会做坏」。定时任务天生会重跑:机器休眠后补跑、网络抖动超时、页面改版导致中途失败、甚至同一个 cron 表达式被注册了两次。如果一次执行不是幂等的,重跑就会变成灾难——重复发帖、重复提交、被平台判定...

我每天清晨 6:00 让一个浏览器自动化任务替我做几件重复的事:登录、签到、把当天能做的积分任务做掉、最后回读积分明细写一份汇报。这套东西跑了十来天,踩得最多的坑不是「能不能做对」,而是「重跑会不会做坏」。


定时任务天生会重跑:机器休眠后补跑、网络抖动超时、页面改版导致中途失败、甚至同一个 cron 表达式被注册了两次。如果一次执行不是幂等的,重跑就会变成灾难——重复发帖、重复提交、被平台判定刷量。


下面是我最终稳定下来的四个落点。


一、先查状态,再决定动作


大多数自动化脚本的写法是「直接执行,失败就重试」。幂等的写法是先问一句:这件事现在处于什么状态?


把每个任务拆成三段:check(读服务端状态)、act(执行动作)、verify(再读一次状态确认变化)。重跑时 check 命中「已完成」就直接跳过,一次 act 都不发。这条看起来朴素,但它把「重跑」从危险操作降级成了无害操作——重跑十次和重跑一次的效果完全一样。


实际收益很直接:签到这种一天只能一次的动作,靠 check 判断「今天是否已签到」,而不是靠「上次执行有没有报错」。


二、给每次执行一个幂等键


check 只能判断服务端状态,判断不了「我本地是不是已经做过」。所以本地还要有一份账:以「日期 + 任务标识」作为幂等键,执行成功后落盘记录,重跑时先读这份账。


键里必须包含日期,否则第二天的任务会被昨天的记录挡住。也建议把「失败」和「未执行」分开记:只有成功才写终态,失败保持可重试,这样才能区分「已经做过」和「还没做完」。


三、失败要分类,不能一律重试


我把失败分两类。第一类是可重试:元素还没渲染、请求超时、偶发 5xx,这类重试一两次基本能过。第二类是不可重试:需要人工验证码、需要人脸识别、需要另一个系统授权,这类重试一百次也没用,只会把账号推到风控前面。


工程上的做法是给每个任务一个重试预算,同一步连续失败两次就跳过,并在汇报里显式写「卡在哪」。这比无限重试诚实,也比静默失败有用——人第二天看到「卡在需要人脸识别」,就知道该自己上场了。


四、成功信号必须来自服务端


这是最容易翻车的一条。点击成功不等于任务完成:按钮点了没反应、表单缺一个必填项、页面静默拦截,都会让脚本以为成功了。


我自己就撞过一次:点「发表」之后页面没有任何跳转也没有报错,脚本记了一笔「成功」,实际文章根本没发出去,当天积分也没到账。更糟的是,因为本地已经记了成功,重跑时被直接跳过,问题要等到第二天对账才暴露。


所以终态只能由服务端事实决定:回读一次列表或明细,确认目标状态真的变了,才算成功。所谓「验证」,就是拿一个和你的点击无关的证据。


一段骨架


把上面四条收成伪代码,大致是这样:


for task in tasks:

    key = today() + ":" + task.id

    if ledger.done(key):

        continue

    if task.check():

        ledger.mark(key, "done")

        continue

    for attempt in range(2):

        if task.act() and task.verify():

            ledger.mark(key, "done")

            break

    else:

        ledger.mark(key, "blocked", task.reason())


写在最后


幂等性听起来是后端接口设计的话题,但任何「会被重复触发的自动化」都躲不开它。区别只是:接口可以用唯一键去重,脚本只能靠自己查状态、记账、核对。


如果你也在跑定时任务,不妨先问自己一句:这条任务今天已经跑过一次了,再跑一次会发生什么?如果答案是「会重复发一篇文章」,那它现在就还不该上定时器。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。