U46 Spring Batch 与 Quartz 持久任务实验
实验使用 Liberica JDK 21、Boot 4.0.9 BOM 管理的 Spring Batch 6.0.6(spring-boot-starter-batch-jdbc)与 Quartz 2.5.2(spring-boot-starter-quartz)、Connector/J 9.7.0;数据库为 DBngin 的本机 MySQL 8.4.7。
实验代码bash
python3 labs/batch-quartz/run.pyrunner 离线使用 Maven 本地缓存并核对依赖版本;每个测试类使用独立 study_u46_* schema,fixture 关闭 Hikari 数据源并 DROP 独有库,runner 再核对 study_u46% 无残留;失败运行不覆盖已有成功证据。运行需要本机 MySQL 监听 127.0.0.1:3306(默认 root 空密码,可用 LAB_DB_USER / LAB_DB_PASSWORD 覆盖,不写证据)。
场景与观察(19 测试,U46 前缀逐字回收):
| 场景 | 观察 |
|---|---|
| Batch 身份 | JOB_NAME + JOB_KEY(identifying 参数排序取 MD5);换 identifying 参数产生新实例,非 identifying 参数忽略 |
| chunk 事务与重启 | chunk=2 第 3 条失败:业务表只有前 2 行,重启从第 3 条续读(总读 10 次含失败块重放) |
| 容错默认 | 不开 faultTolerant() 时 skipLimit 不生效(FAILED,writeSkip=0);开启后坏条目跳过、其余写入 |
| abandon/recover | abandoned 执行不可同实例重启;recover 解除 UNKNOWN 卡死并允许新执行 |
| 并发启动 | 同一实例并发启动只有一个胜出(串行化隔离 + 唯一约束) |
| Quartz 持久化 | JDBC JobStore 跨调度器重启保留 job/trigger(WAITING) |
| 集群互斥 | isClustered=true 两调度器共享触发器只触发一次(QRTZ_LOCKS 两行);非集群模式死实例触发器卡死 |
| 故障接管 | 死实例行被集群检查移除,requestsRecovery 触发器被接管 |
| misfire | CronTrigger FIRE_ONCE_NOW 与 DO_NOTHING、SimpleTrigger SMART 策略各自语义 |
| 并发与停机 | @DisallowConcurrentExecution 使同 job 触发 BLOCKED;waitForJobsToComplete 让运行中任务跑完 |
| Boot 自动配置 | batch 表 9 张、qrtz 表 11 张由 starter 自动创建;ResourcelessJobRepository 未使用 |
资源:入口、POM、Batch 持久化测试、Quartz 持久化测试、Boot 自动配置测试、Batch 夹具、Quartz 夹具、库夹具、MySQL 证据。
边界:单机 MySQL;未运行 JDBC JobStore 的多节点真实跨主机时序、TinySeconds 行锁与 ThreadPool 尺寸调优、Batch 远程分区/远程 chunk、生产容量;misfire 观察用秒级构造时间。