Appearance
Spring Batch 步骤重启配置指南
🚀 让任务中断后智能恢复执行,避免重复处理和数据混乱
🔍 引言:为什么需要配置步骤重启
在 Spring Batch 中,任务(Job
)重启会直接影响步骤(Step
)的执行逻辑。合理配置重启策略能解决以下核心问题:
✅ 防止资源冲突:某些步骤只能执行一次(如初始化操作)
✅ 确保数据完整性:失败后从断点继续处理而非重新开始
✅ 灵活控制流程:不同步骤可设置独立的重启策略
NOTE
本指南基于 Kotlin DSL 实现,避免 XML 配置,采用注解和类型安全配置
⚙️ 一、设置步骤启动限制(Start Limit)
控制步骤的最大执行次数,避免资源被重复初始化。
典型场景
- 步骤会锁定或修改关键资源(如数据库表结构变更)
- 步骤失败后需人工介入才能再次执行
Kotlin 配置示例
kotlin
@Configuration
class BatchConfig {
@Bean
fun step1(jobRepository: JobRepository, transactionManager: PlatformTransactionManager): Step {
return StepBuilder("step1", jobRepository)
.chunk<String, String>(10, transactionManager)
.reader(itemReader())
.writer(itemWriter())
.startLimit(1) // 关键配置:仅允许执行1次
.build()
}
}
WARNING
当超过 startLimit
限制时,会抛出 StartLimitExceededException
。默认值为 Int.MAX_VALUE
(无限次)
🔄 二、重启已完成的步骤(Allow Restart)
强制已完成的步骤在任务重启时再次执行,适用于需重复操作的场景。
典型场景
- 清理临时资源的步骤(如删除处理完的文件)
- 数据校验步骤(每次任务启动都需执行)
Kotlin 配置示例
kotlin
@Bean
fun cleanupStep(jobRepository: JobRepository, transactionManager: PlatformTransactionManager): Step {
return StepBuilder("cleanupStep", jobRepository)
.chunk<File, File>(5, transactionManager)
.reader(fileReader())
.writer(fileDeleter())
.allowStartIfComplete(true) // 关键配置:总是执行
.build()
}
🏈 三、综合示例:足球数据统计任务
通过真实场景演示重启策略的组合应用。
业务需求
playerLoad
:加载球员数据 → 仅执行1次gameLoad
:加载比赛数据 → 每次重启都执行playerSummarization
:统计球员数据 → 最多执行2次
Kotlin 完整配置
kotlin
@Configuration
class FootballJobConfig {
@Bean
fun footballJob(jobRepository: JobRepository,
playerLoad: Step,
gameLoad: Step,
playerSummarization: Step): Job {
return JobBuilder("footballJob", jobRepository)
.start(playerLoad)
.next(gameLoad)
.next(playerSummarization)
.build()
}
// 步骤1:球员数据加载(仅运行1次)
@Bean
fun playerLoad(jobRepository: JobRepository, txManager: PlatformTransactionManager): Step {
return StepBuilder("playerLoad", jobRepository)
.chunk<Player, Player>(100, txManager)
.reader(playerFileReader())
.writer(playerDBWriter())
.build() // 默认不重启
}
// 步骤2:比赛数据加载(总是运行)
@Bean
fun gameLoad(jobRepository: JobRepository, txManager: PlatformTransactionManager): Step {
return StepBuilder("gameLoad", jobRepository)
.chunk<Game, Game>(50, txManager)
.reader(gameFileReader())
.writer(gameDBWriter())
.allowStartIfComplete(true) // 关键配置
.build()
}
// 步骤3:数据统计(最多运行2次)
@Bean
fun playerSummarization(jobRepository: JobRepository, txManager: PlatformTransactionManager): Step {
return StepBuilder("playerSummarization", jobRepository)
.chunk<PlayerStats, Summary>(20, txManager)
.processor(statsProcessor())
.writer(summaryWriter())
.startLimit(2) // 关键配置
.build()
}
}
📊 四、执行流程分析
通过时序图展示三次任务执行过程:
关键行为说明:
首次执行
playerLoad
成功加载 400 名球员gameLoad
处理 11 个比赛文件playerSummarization
5 分钟后失败
第一次重启
playerLoad
被跳过(已完成且未配置重启)gameLoad
处理新增的 2 个比赛文件playerSummarization
30 分钟后再次失败
第二次重启
playerSummarization
被阻止执行(超过 startLimit=2)- 需要人工介入或创建新 JobInstance
💡 最佳实践建议
关键原则
配置选择指南
步骤类型 startLimit allowStartIfComplete 数据库初始化 1 false 临时文件清理 Int.MAX_VALUE ✅ true 核心数据处理 2-5 false 低优先级任务 Int.MAX_VALUE false 故障排查技巧
kotlin// 在监听器中捕获重启异常 class RestartListener : StepExecutionListener { override fun beforeStep(stepExecution: StepExecution) { if (stepExecution.startCount > 1) { logger.warn("步骤多次重启:${stepExecution.stepName}") } } }
CAUTION
避免将 startLimit
设置为 0,这会导致步骤永远无法执行!
🚨 常见问题解决
问题:步骤意外跳过
解决:检查是否同时满足
- 上次执行状态为
COMPLETED
allowStartIfComplete=false
(默认值)
问题:StartLimitExceededException
解决:
- 通过
JobOperator
创建新 JobInstance - 在代码中动态调整限制:kotlin
fun flexibleStep(): Step { val limit = if (isProduction) 3 else 10 return StepBuilder("flexStep", jobRepository) .startLimit(limit) // ... }
通过合理配置步骤重启策略,您的批处理任务将获得:✨ 更强的容错能力 + 🛡️ 数据一致性保障 + ⚡️ 资源利用率优化