鲁棒性定时任务设计7条准则

1 定时任务执行过程可追溯设计

需要知道定时任务跑了没有,跑了多久,跑成功了,还是跑失败了。这些过程数据作为重跑的依据

需要跟进内容:
定时任务执行结果,是成功,失败,还是重试中。 一般重试N次后,状态变为失败。处于失败的定时任务需要监控告警
任务启动时间和结束时间,方便用来跟进定时任务的执行时间,方便做一些性能优化,或者很多任务需要合理编排任务的启动时间。
已重试的次数:用来了解失败的原因
任务处理的数据日期:根据该天的任务有没有执行,一般一天只有一个 任务处理的数据日期

2. 大数据量需分片处理(分布式调度)

分布式计算(分片技术)

断点续跑

解决:定时任务做最小粒度事务控制,并记录状态。比如计息,每个人每天是最小粒度, 完成一条记录一条的状态,挂了重跑,从没跑的地方继续执行。

3. 定时任务的依赖处理,避免上一个没有跑完,下一个却跑了。

问题:上一个没跑完,下个就跑了,或者上一个跑失败了,下一个也跑了。
解决:
1 合并定时任务
2 使用定时任务依赖链。链条中一个失败或者没有跑完,后面不能跑。如使用Airflow构建DAG

4 支持手动重跑(支持最小粒度的重试)

CREATE TABLE task_execution_logs (
    -- 基础信息
    id BIGSERIAL PRIMARY KEY,
    task_name VARCHAR(128) NOT NULL,        -- 任务名称(如 'daily_city_transaction_agg')
    execution_date DATE NOT NULL,           -- 任务处理的数据日期(如 '2023-10-01')
    start_time TIMESTAMP NOT NULL,          -- 任务启动时间
    end_time TIMESTAMP,                     -- 任务结束时间
    execution_status VARCHAR(32) NOT NULL,  -- 状态(RUNNING/SUCCESS/FAILED/RETRYING)
    retry_count INT DEFAULT 0,              -- 已重试次数
    
    -- 业务上下文
    processed_cities JSONB,                 -- 已处理的城市列表(用于断点续跑),不同业务不一样
    total_cities INT,                       -- 总需处理城市数
    success_cities INT,                     -- 成功处理城市数
    failed_cities JSONB,                    -- 失败城市及原因(如 [{"city": "上海", "error": "超时"}])
    
    -- 系统信息
    executor_node VARCHAR(64),              -- 执行节点标识(IP 或容器ID)
    error_message TEXT,                     -- 错误堆栈信息
    parameters JSONB                        -- 任务参数(如分片策略、重试配置)
);

5 防止重叠设计

定时任务重叠的分布式锁

问题:redis定时任务锁,超时时间不好控制,设置太短没效果,太长影响后续的执行。如何实现,当任务有异常的时候解锁,如果任务
没有异常等任务执行完解锁

对于Java项目,推荐直接使用Redisson库的RLock,它内置了看门狗自动续期机制,简化开发:

核心思路
​​唯一标识锁​​:使用唯一值(如UUID)作为锁的值,确保只有锁的持有者能释放。
​​自动续期机制​​:后台线程定期延长锁过期时间,防止任务未完成时锁自动释放。
​​主动释放锁​​:无论任务成功或异常,都在finally块中主动释放锁。
​​兜底超时​​:设置合理超时时间作为安全网,防止程序崩溃导致死锁。lock_timeout应大于任务正常执行的最长时间。

RedissonClient redisson = Redisson.create();
RLock lock = redisson.getLock("my_task_lock");

try {
    lock.lock();  // 默认30秒超时,看门狗每10秒续期
    // 执行任务...
} finally {
    lock.unlock();
}

通过态机设计 防止定时任务重叠

-- 在任务表中添加状态锁字段
UPDATE task_scheduler SET status = 'RUNNING' 
WHERE task_name = 'daily_city_transaction_agg' AND status = 'PENDING';
-- 执行前抢占锁,成功后执行业务逻辑

6 自动补偿机制(失败重试几次,

  • 自动重试:配置框架级重试(如 Spring Retry 或 Celery Retry),设定规则:
    • 重试次数:3~5 次,避免无限重试。
    • 重试间隔:指数退避(如 1min, 5min, 15min)。
  • 人工介入兜底:重试失败后标记任务为 MANUAL_INTERVENTION_NEEDED,触发告警。

7 异常告警

参考资料

posted @ 2025-04-24 21:34  向着朝阳  阅读(47)  评论(0)    收藏  举报