
本文介绍如何通过 Spring Batch 的 FlowBuilder.stopAndRestart() 与自定义 JobExecutionDecider 实现外部服务驱动的步骤级动态暂停与恢复,确保 JobRepository 状态准确同步,避免全量重启导致的数据重复处理。
本文介绍如何通过 spring batch 的 `flowbuilder.stopandrestart()` 与自定义 `jobexecutiondecider` 实现外部服务驱动的步骤级动态暂停与恢复,确保 `jobrepository` 状态准确同步,避免全量重启导致的数据重复处理。
Spring Batch 原生不支持运行时“暂停”(pause)单个 Step 的语义(即线程挂起、状态保持、后续无缝恢复),但提供了符合生产级可靠性的替代方案:停止(stop)+ 条件化重启(restart)。该机制不仅满足外部服务动态干预的需求,还能保证 JobRepository 中 JobExecution 和 StepExecution 状态(如 STOPPED)严格一致,且支持从断点续处理(基于 readCount/writeCount 恢复)。
核心思路是利用 Spring Batch 的 决策流(Decider)+ 流控制(FlowBuilder) 组合,将“是否继续当前 Step”的判断权交由外部服务,在每一步执行前或执行后进行实时决策。
✅ 推荐实现方式:stopAndRestart() + 自定义 JobExecutionDecider
以下是一个完整示例,展示如何在 Step A 后插入一个决策点,由外部服务返回是否应暂停作业:
@Bean
public Job pauseableJob(JobRepository jobRepository, JobLauncher jobLauncher,
PlatformTransactionManager transactionManager) {
return new JobBuilder("pauseableJob", jobRepository)
.start(stepA())
.next(decideToPause()) // 决策点:调用外部服务判断
.build();
}
@Bean
public Step stepA() {
return new StepBuilder("stepA", jobRepository)
.<String, String>chunk(10, transactionManager)
.reader(itemReader())
.processor(itemProcessor())
.writer(itemWriter())
.build();
}
// 自定义 Decider:委托给外部服务判断是否暂停
@Bean
public JobExecutionDecider decideToPause() {
return (jobExecution, stepExecution) -> {
// ✅ 关键:调用你的外部服务(如 REST API、gRPC、消息队列)
boolean shouldPause = externalPauseService.shouldPause(
jobExecution.getJobInstance().getJobName(),
stepExecution.getStepName(),
jobExecution.getId()
);
if (shouldPause) {
// 返回 STOPPED 状态,触发 stopAndRestart
return new FlowExecutionStatus("STOPPED");
} else {
// 继续执行下一个 Step(如 stepB)
return new FlowExecutionStatus("CONTINUE");
}
};
}
// 构建含暂停语义的 Flow:当 Decider 返回 STOPPED 时,自动停止并允许后续重启
@Bean
public Flow pauseableFlow() {
return new FlowBuilder<SimpleFlow>("pauseableFlow")
.start(stepA())
.stopAndRestart(decideToPause()) // ← 核心:绑定 Decider 到 stopAndRestart
.from(decideToPause()).on("STOPPED").to(stepA()) // 可选:暂停后仍从 stepA 续跑(需配置 restartable=true)
.from(decideToPause()).on("CONTINUE").to(stepB())
.end();
}⚠️ 注意事项:
stopAndRestart()不会阻塞线程或冻结 JVM 状态,而是原子性地将StepExecution状态设为STOPPED,并提交事务,使JobRepository持久化该状态;- 重启时,Spring Batch 会自动识别
STOPPED状态,并依据StepExecution.getReadCount()和getWriteCount()从上次中断位置继续读写(前提是ItemReader支持保存/恢复状态,如JdbcCursorItemReader、FlatFileItemReader默认支持);- 所有参与暂停/恢复的
Step必须声明为restartable(true)(默认为true),并在Job配置中启用重启能力;- 外部服务调用应具备幂等性与超时控制,避免因网络抖动导致误判;建议增加重试与降级逻辑(如默认
CONTINUE)。
❌ 不推荐方案说明
-
线程休眠(
Thread.sleep()):破坏 Spring Batch 的事务边界与监控能力,JobRepository状态无法反映真实运行态(仍为STARTED),且易引发超时、资源泄漏与集群调度异常; -
重写
Step接口或核心类:违反框架扩展契约,升级兼容性差,且无法保证JobRepository状态一致性,维护成本极高。
✅ 总结
真正符合 Spring Batch 设计哲学的“暂停”不是线程级挂起,而是状态驱动的可控终止与智能续跑。通过 stopAndRestart() 结合外部服务决策,你既能获得细粒度控制力,又能完全复用框架的持久化、重启、断点续传能力。最终效果是:作业在任意 Step 后可被标记为 STOPPED,外部系统随时调用 JobOperator.restart(jobExecutionId) 即可从精确断点恢复——零数据丢失、零重复处理、零状态不一致风险。

















