并发任务调度引擎
# 案例 04 · gocron · 并发任务调度并发任务调度器
卷二第 4 篇 · 难度 ⭐⭐⭐⭐ · 预估 12 小时 · 字数目标 ~2 万字 · 代码量 ~2500 行
# 目录介绍
# 00.案例元信息
| 项目 | 内容 |
|---|---|
| 难度 | ⭐⭐⭐⭐ |
| 预估时长 | 12 小时 |
| 前置章节 | 卷一第 8-18 章(并发三件套 + 错误 + 工程化) |
| 主题领域 | 并发系统 + 调度器 + 任务隔离 + Trace 可观测 |
| 最终产物 | gocron 库 + demo 二进制(含两个示例任务) |
| 0 第三方库 | ✅(仅 stdlib) |
功能列表:
- 类似 cron 但支持秒级;任务定义用 Go 函数(不是 shell 命令)
- 调度精度:四叉堆排序;自己实现一个简化版 P 本地 timer
- worker pool:固定 N 个 worker、动态扩缩容
- 任务隔离:单任务 panic 不影响调度器(recover)
- 取消传播:父任务取消,所有子任务级联取消
- trace:每个任务执行链路可视化(输出 jaeger 兼容格式)
# 01.需求拆解
⏳ 待撰写:业务背景(为什么要自己写不用 robfig/cron)+ 性能目标(百万任务时单机表现)+ 不做什么(不做分布式 / 不做持久化)。
# 02.架构设计
⏳ 待撰写:mermaid 模块图(Scheduler → TimerHeap → Dispatcher → WorkerPool)+ 时序图 + 关键决策(四叉堆 vs 红黑树 / 一个全局锁 vs 多锁)。
# 03.核心数据结构
⏳ 待撰写:
Job/Trigger接口 /TimerHeap/Worker/Trace。
# 04.关键流程逐段实现
⏳ 待撰写:按"功能切片"展示完整可跑代码,约 8500 字。
- 4.1 项目布局(5 个 package)
- 4.2 Trigger 接口(cron / interval / once 三种)
- 4.3 四叉堆 Timer(用泛型)
- 4.4 Scheduler 主循环(select + nil channel 妙用)
- 4.5 WorkerPool(固定 + 弹性 + recover 隔离)
- 4.6 Context 级联取消(父 cancel → 所有子任务)
- 4.7 Trace 收集与 jaeger 输出
- 4.8 demo:HTTP 健康检查 + 文件备份
# 05.反模式对照
⏳ 待撰写,6-10 条 ❌ vs ✅(本案例反模式最多)。
- ❌
time.After在循环里泄漏 → ✅time.NewTimer+Stop- ❌ 用 sleep 轮询任务队列 → ✅
time.Timer+select唤醒- ❌ panic 直接挂 worker → ✅
defer recover转 error- ❌
for { go work() }无界 goroutine → ✅ 固定 worker 数 + 任务 channel- ❌
chan struct{}close 当广播但二次 close → ✅ context.Done() 或 sync.Once- ❌ ctx 存进 struct → ✅ 始终作为函数第一参数
# 06.测试与基准
⏳ 待撰写:调度精度测试(误差 < 10ms)+ 百万任务 benchmark + race detector 跑全套测试。
# 07.卷一章节反向索引
| 本案例小节 | 卷一章节 |
|---|---|
| 4.2 Trigger 接口 | 第 10 章 |
| 4.3 四叉堆 + 泛型 | 第 16 章 |
| 4.4 select + nil channel | 第 13 章 |
| 4.5 WorkerPool + WaitGroup | 第 12、14 章 |
| 4.5 panic recover | 第 11 章 |
| 4.6 context 级联 | 第 12 章 |
| 4.7 errors.Join 聚合 | 第 11、18 章 |
# 08.拓展挑战
⏳ 待撰写 3-5 个进阶题。
- 把 trace 接到真实 OpenTelemetry(思考 stdlib 边界)
- 加任务持久化(崩溃重启不丢任务)→ 提前预演卷三 KV
- 把单机改造成 leader-follower(思考 raft)
⬅ 上一篇:案例 03 · goshort 短链服务
➡ 下一篇:案例 05 · gosite 静态博客生成器(毕业设计)
上次更新: 2026/06/11, 19:20:46