问题摘要
- 几天前在生产上遇到了一个job失败,重启后csv文件不可写的问题.当时认为是batch的重启机制导致的.后来经过分析后是因为1.batch用了集群环境2.重启机制两方面导致的;
问题分析
- 前情提要:job中的一个step处于FAILED状态,step是一个chunk写成的,包括reader,processor,writer(
FlatFileItemWriter
)三部分,其中processor抛出了异常,导致step失败; - batch系统在运行时会将失败时step的状态保存到
batch_step_execution_context
中
{"map":[{"entry":[
{"string":"FlatFileItemWriter.current.count","long":228},
{"string":"FlatFileItemWriter.written","long":0},
{"string":["batch.taskletType","org.springframework.batch.core.step.item.ChunkOrientedTasklet"]},
{"string":"selectBdBizOver.read.count","int":0},
{"string":["batch.stepType","org.springframework.batch.core.step.tasklet.TaskletStep"]}
]}]}
该表保存了step运行退出时的状态 (成功的话不会保存),
step_execution
表示该step的唯一标识,short_context
表示执行上下文,会在step重启时用于恢复; 其中FlatFileItemWriter.current.count
为上一次退出时csv文件总共写入了多少字节,step恢复时会从该位置进行恢复;FlatFileItemWriter.written
表示csv内的内容写了多少(除去表头的内容)
- 回到正题,出现File is not writable的异常是在batch自带的一个文件工具类中
该工具类中一个很重要的参数是
restarted
决定着是否重新生成一个csv文件,当一个step处于FAILED状态并且FlatFileItemWriter
中的属性shouldDEleteIfEmpty
为false时,该值为true,意味着不会重新生成文件,而且会直接走到54行if(!file.canWrite())
进行判断该路径是否可达可写,在此时出现了问题.
因为在生产上使用的集群环境,job第一次开始时是在第一台服务器上,那时已经生成好了包含表头的csv文件.而在重启时job是在第二台服务器上运行的.
又因为job的重启机制(restarted
为true)导致该step不会重新生成csv文件,而是直接检查文件路径是否可写,发现文件并不存在,于是抛出了异常
- 如果是在同一台服务器上重启则不会出现该问题
解决方法
- 将
shouldDeleteIfEmpty
属性设为true
,系统会在恢复时检查该属性,如果为true
且上文batch_step_execution_context
表中的short_context
–FlatFileItemWriter.written
为0
(说明csv文件内容为空)就会将restarted
属性设为false
fileUtils
会重新生成一份csv文件(FlatFileItemWriter
的359行会在初始化写入缓存时调用fileUtils.setOutputFile(...)
方法,如下图所示).- 该解决存在缺陷,如果csv文件内容不为空的话,
restarted
属性还是为true