定时任务没有产生预期结果,不代表调度器一定没有启动。可能是触发事件未生成、进程或调度线程异常、节点门禁拒绝执行、分布式锁阻塞,也可能任务完成计算却没有提交业务结果。排查时先按一个计划周期串起状态证据,再决定是否补偿;盲目重启或手工补跑可能制造重复写入。🧭
首先用计划周期ID查询调度记录。没有触发记录,重点看调度器心跳、时区、时钟同步、计划表达式和调度分片;有触发但没有实例启动记录,检查队列投递、进程资源和启动错误;已有开始记录但无结束状态,再看任务步骤、依赖调用、锁续租和提交记录。不要仅用“最近一次日志时间”判断,因为日志采集延迟或写入失败会形成假缺口。
同一时间戳要说明时区和精度。容器与数据库时钟偏差可能让事件顺序看起来倒置。可观察last_trigger_at、last_start_at、心跳时间和计划周期标识,并与进程启动时间对照。若调度器状态只在进程内存中,滚动重启或实例迁移会丢失未持久化的触发信息。🕰️
许多任务只允许某类节点、租户分片或区域执行,也可能受维护开关、部署冻结窗口影响。核对目标周期触发时有哪些节点符合标签,实际生效的功能开关和角色是什么,是否存在“所有节点都不符合”的空窗。节点身份变化后,注册信息未更新可能让新节点无法接管;远程配置更新只影响部分副本,也会造成间歇性失效。
把调度决策记录下来:候选节点集合、排除原因、最终执行者及所用配置版本。避免在日志中输出秘密值,只记录配置版本或非敏感摘要。门禁拒绝应有可区分的状态码或结构化原因,不能只记成通用失败。🚦
锁记录应能定位持有者、获取时间、租约到期和续租状态。先确认持有者实例是否存活、对应工作线程是否仍在推进,再判断租约是否过期。数据库会话锁可能由未结束事务持有;分布式租约可能因长时间暂停而过期,但旧线程恢复后仍继续运行。若下游没有世代号或幂等约束,释放锁并不自动停止旧持有者,可能造成双重写入。
正确的接管设计包含递增世代号或栅栏令牌,旧世代提交会被拒绝;任务还应使用业务周期构造幂等键,使同周期重试只产生一个逻辑结果。对任务来说,“日志打印开始”不等于执行成功,必须查看事务提交、结果行或下游确认。🔐
不要运行缺少周期边界的全量任务脚本来“试试看”,也不要仅为了让调度记录变绿而重置状态。测试环境可模拟节点退出和租约失效,验收新节点只在正确接管后推进,旧世代提交被拒绝,重复触发不产生重复副作用。生产恢复后,完成数应能对应业务结果数,失败和积压周期均有明确处理状态。✅