定时任务显示成功却没执行:Linux文件锁与中断恢复的排查方法

举报
yd_235343804 发表于 2026/11/05 18:34:22 2026/11/05
【摘要】 从主任务与保活的锁冲突出发,说明有界等待、外部提交前日志、只读审计隔离与中断恢复的实现和验收方法。

定时脚本每天触发,不代表每天的业务都执行过。一个容易被忽略的情况是:保活进程先拿到了浏览器锁,主任务遇到锁冲突后直接以退出码 0 结束。服务管理器记录“成功”,实际动作却一次也没运行。下面用 Linux 文件锁和持久化执行记录说明怎样区分这种情况。

1. 先区分两类进程的等待策略

保活是机会型任务:浏览器正在执行主任务时,这轮保活可以跳过。每日主任务则有明确的完成要求,遇到同一把锁时应在有限时间内等待;超时仍未拿到锁,要向调度器返回失败。把两者都写成非阻塞抢锁,并在冲突时返回成功,会把“未执行”混入“已完成”。

等待时间没有适用于所有业务的固定值。可以先测量保活实际占锁时间,再给主任务设置一个上限,例如 180 秒。该上限还必须小于服务总超时,否则进程可能刚拿到锁就被服务管理器终止。

2. 有界等待的最小实现

下面的例子运行于 Linux,锁文件目录需要由运行用户提前创建。进程间互斥依赖的是内核文件锁,不能用“文件是否存在”判断是否有人持锁。

import fcntl
import time
from contextlib import contextmanager

@contextmanager
def exclusive_lock(path, wait_seconds=180):
    with open(path, "a", encoding="utf-8") as handle:
        deadline = time.monotonic() + wait_seconds
        acquired = False
        try:
            while not acquired:
                try:
                    fcntl.flock(handle, fcntl.LOCK_EX | fcntl.LOCK_NB)
                    acquired = True
                except BlockingIOError:
                    if time.monotonic() >= deadline:
                        raise TimeoutError("browser lock unavailable")
                    time.sleep(0.2)
            yield
        finally:
            if acquired:
                fcntl.flock(handle, fcntl.LOCK_UN)

超时计算使用 time.monotonic(),避免系统校时影响等待长度。无论业务函数抛出异常还是正常返回,文件描述符都会关闭。不要在仍有进程可能持锁时删除并重建锁文件:新旧进程可能锁住不同的 inode,互斥就失效了。

3. 把业务结果写成可恢复的记录

锁只解决“不要同时操作浏览器”,并不解决“提交后断线还能否重试”。一次外部操作至少有三个关键时刻:准备执行、已开始可能产生外部影响的操作、已取得业务确认。应在点击提交前持久化第二个时刻,而不是等待成功页面出现后才记日志。

例如创建工单时,请求可能已经到达服务端,但浏览器在跳转前断开。下轮启动看到“提交已开始、结果未确认”,应该先查工单列表或服务端幂等键。只有证据表明没有产生提交,才允许重新执行。直接把超时解释为失败并重发,会制造重复工单。

记录键可由账号、业务对象和执行周期组成。账号是隔离边界,周期是去重边界;昨天的完成记录不应阻止今天的任务,本周未确认的提交则不应因为换了一天就被重复发送。

4. 不让只读检查覆盖执行事实

建议分别保存“最近一次执行”和“最近一次审计”。假设上午主任务漏跑,下午健康检查成功刷新了页面,如果恢复程序只读取一个统一的 latest 文件,就可能把这次审计误当成业务已经执行。执行报告应包含 run ID、启动时间、退出状态和外部操作记录;审计报告只反映观察时刻的平台状态。

多个进程写 JSON 时,临时文件也要唯一。固定使用 report.json.tmp 会产生覆盖竞争。可在目标目录用 tempfile.mkstemp 创建独立临时文件,写入、刷新后再用 os.replace 原子替换。多写者仍需互斥或版本检查,原子替换本身不会防止旧报告覆盖新报告。

5. 如何证明修复有效

  • 先让辅助进程持锁,启动主任务,确认它确实等待;释放后只执行一次。
  • 让持锁时间超过上限,确认主任务返回非零,并保留可诊断的超时信息。
  • 在外部提交后模拟进程中断,确认重启走对账分支,而不是再次提交。
  • 运行只读审计,确认恢复程序所读的执行时间没有被刷新。

验收最终应落到业务系统:对象是否真的创建、状态是否真正完成、是否存在重复记录。服务退出码和 HTTP 200 都是中间证据,不能代替业务确认。

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

评论(0)

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

全部回复

上滑加载中

设置昵称

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

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

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