Skip to content

15 · 定时任务 ​

日报推送、账单结算、缓存预热、数据清理——这些不靠用户触发,靠定时任务。本篇从单机 cron 讲到分布式调度,并用你正在接触的 XXL-Job 做类比。

速览卡 ​

项内容
一句话定位定时任务从"单机能跑"到"分布式不重不漏"的完整台阶
核心内容cron 与进程内调度、单机方案(APScheduler)、分布式调度的核心问题、XXL-Job 概念类比、幂等与补偿
读完能回答定时任务该放哪跑?多实例会不会重复执行?XXL-Job 那些概念到底解决什么问题?
相关板块C06 · 异步与消息 | C04 · 一致性与事务 | C17 · 部署与 CI/CD

一、先分清:定时任务 ≠ 消息队列 ​

维度定时任务消息队列
触发时间(cron)事件(有消息就执行)
典型场景每天 3 点结算下单后发通知
延迟固定节奏即时

两者常混用,但本质不同:定时任务是"到点干活",MQ 是"有活就干"。详见 C06。


二、单机方案 ​

2.1 系统 cron ​

cron
0 3 * * *  /usr/bin/python /app/jobs/settlement.py

简单、可靠,但只能单机、没有失败重试、没有监控。适合"不重要的维护任务"。

2.2 进程内调度:APScheduler ​

python
from apscheduler.schedulers.background import BackgroundScheduler

scheduler = BackgroundScheduler()


@scheduler.scheduled_job("cron", hour=3, minute=0)
def settlement():
    ...


scheduler.start()

优点:代码里定义、可重试。缺点:多实例部署会重复执行——这是迈向分布式的第一步。


三、分布式调度的核心问题 ​

应用实例从 1 个变 3 个后,定时任务会遇到三个问题:

问题现象解法
重复执行三个实例各跑一遍分布式锁 / 主节点选主
没有重试跑挂就丢了任务记录 + 重试策略
没有监控悄悄失败没人知道日志/告警接入

3.1 用 Redis 分布式锁防重复 ​

python
def run_once(key, ttl=60):
    # 拿到锁才执行,拿不到说明别的实例在跑
    if not redis.set(f"job:{key}", "1", nx=True, ex=ttl):
        return
    do_work()

这是"自研调度"的最小方案,够小项目用。规模再大就交给调度框架。


四、XXL-Job 概念类比 ​

你正在接触的 XXL-Job 就是"分布式调度框架",它的核心概念用前端思维解释:

XXL-Job 概念是什么前端类比
执行器(Executor)真正干活的服务worker 进程
调度中心(Admin)管"什么时候派活"任务调度器/消息中心
任务 / Handler一个可调度的函数一个可执行的任务函数
路由策略活派给哪个实例(轮询/一致性哈希/广播)负载均衡策略
失败重试失败自动重跑 N 次重试机制
分片广播大任务拆给多个实例各干一部分MapReduce 思想

一句话:XXL-Job = 定时任务的"调度中心 + 注册中心 + 重试/监控",你写的 @XxlJob("xxx") 就是注册一个可被调度执行的任务。


五、幂等与补偿 ​

无论单机还是分布式,定时任务必须幂等——因为可能重试、可能重复执行:

手段说明
唯一约束任务写库时用业务唯一键,重复执行撞约束即跳过
状态机任务记录从 pending → running → done,重跑只处理 pending
可重入每个步骤设计成"重复执行结果一样"
python
def settlement():
    for order in pending_orders():
        if mark_done(order.id):      # UPDATE ... WHERE status='pending'
            do_settle(order)         # 只有这次抢到 pending 的才处理

与 C04 的幂等设计是一套方法论,定时任务只是更依赖它。


自检清单 ​

  • [ ] 能说清定时任务与消息队列的区别
  • [ ] 能用 APScheduler 或系统 cron 跑一个单机任务
  • [ ] 能说清多实例下"重复执行"问题及分布式锁解法
  • [ ] 能说出 XXL-Job 的执行器/调度中心/路由策略各解决什么
  • [ ] 会给任务做幂等(状态机或唯一约束)

常见误区 ​

  • 误区:定时任务多实例跑没事。 不防重会重复扣款、重复发券,必须幂等或加锁。
  • 误区:任务失败就完了。 要有重试和告警,否则线上悄悄丢任务。
  • 误区:拿 MQ 当定时任务用。 场景不同,延迟队列不等于定时调度。
  • 误区:定时任务只能放后台进程。 也可以接在请求里触发检查,但别阻塞主流程。

动手练习 ​

练习 A:每日结算 ​

写一个"每日 3 点结算订单"的 APScheduler 任务,带 pending → done 状态机防重。

练习 B:分布式锁防重 ​

起两个进程同时跑同一个任务,用 Redis 锁验证只有一个真正执行。

练习 C:映射 XXL-Job ​

对照你项目的 @XxlJob 代码,标注每个 handler 对应本文的哪个概念,写一段解释。


相关文档 ​

本区 ​

其他域 ​