跳转到内容

15. Task Scheduler 任务调度器

你做了一个番茄钟应用,需要在浏览器端实现定时提醒:25分钟工作 + 5分钟休息循环。

用纯JavaScript实现定时任务管理,支持创建、取消、查看定时任务。

帮我用纯HTML+JavaScript实现一个浏览器端任务调度器:
1. 番茄钟核心:
- 25分钟倒计时 → 弹出休息通知
- 5分钟休息倒计时 → 弹出工作通知
- 每4个番茄钟一次15分钟长休息
2. 通用定时任务管理:
- 创建任务:名称、执行时间(延迟X秒 或 指定时刻)、是否重复
- 任务列表显示:名称、下次执行时间、状态(等待/已执行/已取消)
- 取消任务、暂停任务
3. 调度器实现:
- 内部用setTimeout实现
- 一个调度器对象管理所有任务ID和timer引用
- 提供add/cancel/pause/resume方法
4. 持久化:
- 任务定义存localStorage
- 刷新页面后重新计算剩余时间,重新调度
5. Notification API:
- 任务到期时用浏览器Notification弹通知
- 请求通知权限
纯原生JS,不用框架。
  • 番茄钟正确倒计时并循环
  • 定时任务到时间触发通知
  • 取消任务后不再触发
  • 刷新页面后任务恢复调度
  • 浏览器通知正确弹出
  • setTimeout/setInterval的精度限制 → Module: JavaScript事件循环
  • 浏览器后台标签页的定时器节流 → Module: 浏览器API
  • 简单调度器的数据结构设计 → Module: 任务调度基础

番茄钟改为多用户版本,需要服务端定时任务:如每天8点发送今日任务提醒、每周生成统计报告。

用Go实现后端定时任务调度器,支持cron表达式和goroutine执行。

帮我用Go+Gin实现后端任务调度器:
1. Cron调度器:
- 解析标准cron表达式(分 时 日 月 周)
- 用time.Ticker驱动,每分钟检查一次是否有任务到期
- 任务到期时启动goroutine执行
2. 内置任务:
- 每天08:00:给所有用户发"今日待办"提醒
- 每周一09:00:生成上周统计报告
- 每小时:清理过期临时文件
3. 管理API:
- GET /api/tasks — 查看所有定时任务
- POST /api/tasks — 创建新任务(名称+cron表达式+handler)
- DELETE /api/tasks/:id — 删除任务
- POST /api/tasks/:id/trigger — 手动触发一次
4. 执行日志:
- 内存中记录最近100次执行记录
- 记录:任务名、开始时间、耗时、状态(成功/失败)、错误信息
5. 优雅停止:
- 收到SIGTERM时等待正在执行的任务完成(最多30秒)
- context取消机制
不要使用第三方cron库,自己实现cron表达式解析。
  • cron表达式正确触发任务
  • 手动触发立即执行
  • 执行日志记录完整
  • 优雅关闭等待任务完成
  • goroutine没有泄漏
  • Cron表达式语法和解析 → Module: 定时调度
  • Go goroutine生命周期管理 → Module: Go并发
  • 优雅关闭(graceful shutdown) → Module: 服务生命周期

服务重启后所有定时任务丢失。需要任务持久化到数据库,重启后自动恢复。同时支持一次性定时任务。

任务定义存数据库,服务启动时加载,支持周期任务和一次性任务。

在V2基础上实现持久化任务调度:
1. 数据库模型(PostgreSQL):
- scheduled_tasks表:
id, name, type(cron/once), expression, handler_name,
payload(JSON), next_run_at, last_run_at, status(active/paused/completed),
retry_count, max_retries, created_at
2. 任务类型:
- 周期任务(cron):按cron表达式循环执行
- 一次性任务(once):指定时间执行一次,完成后标记completed
- 延迟任务:N分钟/小时后执行(转换为一次性任务)
3. 调度器逻辑:
- 启动时从DB加载所有active任务
- 每30秒扫描一次next_run_at <= now的任务
- 执行后更新last_run_at,计算并更新next_run_at
- 一次性任务执行后标记completed
4. 失败重试:
- 任务执行失败时retry_count+1
- 未超过max_retries则延迟重新调度(指数退避)
- 超过max_retries标记为failed并告警
5. API扩展:
- POST /api/tasks/delay — 创建延迟任务("30分钟后提醒我")
- GET /api/tasks/history — 执行历史(分页)
- PUT /api/tasks/:id/pause — 暂停任务
- PUT /api/tasks/:id/resume — 恢复任务
handler_name映射到Go函数,用map[string]HandlerFunc注册。
  • 服务重启后任务自动恢复调度
  • 一次性任务准时执行且只执行一次
  • 失败任务按指数退避重试
  • 暂停的任务不执行,恢复后继续
  • 执行历史可分页查询
  • 任务调度的持久化设计 → Module: 持久化存储
  • 指数退避重试策略 → Module: 容错模式
  • 任务状态机(active→paused→completed→failed) → Module: 状态机设计

V4 — 1000个用户:分布式锁防重复

Section titled “V4 — 1000个用户:分布式锁防重复”

服务部署了3个实例做负载均衡,定时任务被每个实例都执行了一次,导致用户收到3封重复邮件。

用Redis分布式锁保证同一时刻只有一个实例执行某个定时任务。

在V3基础上实现分布式任务调度:
1. Redis分布式锁:
- 任务执行前获取锁:SET task_lock:{task_id} {instance_id} NX EX 300
- 获取到锁才执行,否则跳过
- 任务完成后释放锁(Lua脚本保证只释放自己的锁)
- 锁超时时间 = 任务预期执行时间 * 2
2. 实例注册:
- 每个实例启动时注册到Redis:instance:{id} → {ip, port, started_at}
- 心跳续约,30秒过期
3. 任务分配策略:
- 方案A(抢占式):所有实例同时尝试获取锁,抢到的执行
- 方案B(分配式):按task_id % 实例数分配,只有被分配的实例尝试
- 默认用方案A(更简单),方案B作为优化选项
4. 任务执行监控:
- 记录哪个实例执行了哪个任务
- 检测任务执行超时(超过锁时间还没完成)
- 超时任务告警
5. 看门狗机制:
- 长时间任务自动续约锁
- 每隔锁时间1/3续约一次
- 任务异常退出时锁自动过期释放
提供docker-compose.yml:3个服务实例 + Redis + PostgreSQL。
  • 3个实例只有1个执行了定时任务
  • 执行实例宕机后锁自动过期,其他实例接管
  • 看门狗正确续约长任务的锁
  • 任务执行记录显示正确的实例ID
  • 方案A和方案B都能正确工作
  • Redis分布式锁的正确实现(SET NX EX + Lua释放) → Module: 分布式锁
  • 锁续约和看门狗机制(类Redisson) → Module: 锁的高级用法
  • 分布式任务调度的核心难题 → Module: 分布式调度

业务变复杂了:生成月报需要先统计用户数据、再汇总、再生成PDF、最后发邮件。这些步骤有先后依赖关系。

实现DAG(有向无环图)任务编排,支持依赖调度、并行执行、失败重试。

在V4基础上实现DAG任务编排:
1. DAG定义:
- workflows表:id, name, dag_json, status, created_at
- dag_json示例:
{
"nodes": ["统计数据", "生成汇总", "生成PDF", "发送邮件"],
"edges": [["统计数据","生成汇总"], ["生成汇总","生成PDF"], ["生成PDF","发送邮件"]]
}
- 支持并行:如"统计用户"和"统计订单"可并行,"汇总"依赖二者
2. DAG执行引擎:
- 拓扑排序确定执行顺序
- 入度为0的节点可并行执行
- 节点完成后检查下游节点是否所有上游都完成
- 任何节点失败 → 整个workflow标记failed(可选继续)
3. 节点执行:
- 每个节点是一个handler函数
- 上游输出可作为下游输入(通过context传递)
- 支持超时取消(单节点超时 + 整体超时)
4. 失败处理:
- 节点级重试(独立于workflow重试)
- 从失败节点重跑(不重跑已成功的节点)
- 手动跳过失败节点继续执行
5. API:
- POST /api/workflows — 创建workflow
- POST /api/workflows/:id/run — 执行
- GET /api/workflows/:id/status — 查看执行状态(每个节点的状态)
- POST /api/workflows/:id/retry — 从失败节点重试
6. 可视化:返回DAG图的节点和边数据,前端可渲染
提供一个完整的"月报生成"workflow示例。
  • DAG拓扑排序正确
  • 无依赖的节点并行执行
  • 上游失败时下游不执行
  • 从失败节点重试不重跑已成功节点
  • 节点超时正确取消
  • DAG和拓扑排序 → Module: 图算法
  • 任务编排的设计模式 → Module: 工作流引擎
  • context在超时控制中的作用 → Module: Go context

公司有大量定时任务和工作流,需要统一的调度平台。任务量波动大,需要弹性伸缩worker。

构建弹性调度平台:动态worker池、优先级队列、延迟任务、完整监控。

设计并实现弹性任务调度平台:
1. 架构分层:
- Scheduler(调度器):负责任务触发和分配
- Queue(任务队列):Redis优先级队列
- Worker Pool(执行器池):可动态扩缩容
- Dashboard(监控面板):实时状态
2. 优先级队列:
- 使用Redis Sorted Set实现优先级队列
- 优先级:critical(0) > high(1) > normal(2) > low(3)
- 同优先级按FIFO
- 延迟任务:score = 执行时间戳,到时间才出队
3. 动态Worker池:
- Worker向Scheduler注册,汇报能力(CPU/内存/支持的任务类型)
- Scheduler根据队列积压量动态调整Worker数量
- 伸缩策略:积压>100且持续1分钟 → 扩容;空闲5分钟 → 缩容
- Worker上限/下限可配置
4. 任务路由:
- 按任务类型路由到特定Worker(如GPU任务只给GPU Worker)
- 按负载均衡分配
- 亲和性调度:相同数据的任务优先分配到同一Worker(缓存友好)
5. 监控面板:
- 实时数据:队列深度、各优先级任务数、Worker数和负载
- 历史趋势:任务执行量、平均耗时、失败率
- 告警:队列积压、Worker离线、任务失败率飙升
6. 延迟任务:
- 支持"30分钟后执行""每天早上8点执行"
- 延迟任务存Redis ZSet,score=执行时间戳
- 到期扫描转移到执行队列
重点实现优先级队列和动态Worker池的伸缩逻辑。
  • critical任务总是优先执行
  • 延迟任务准时进入执行队列
  • 积压增加时Worker自动扩容
  • 空闲时Worker自动缩容
  • 监控面板实时显示队列和Worker状态
  • 优先级队列的实现(Redis Sorted Set) → Module: 数据结构应用
  • 弹性伸缩策略设计 → Module: 自动扩缩容
  • Worker Pool模式 → Module: 并发模型
  • 调度系统的分层架构 → Module: 平台化思维