Spring Batch 3 - Job异常处理

上篇文章中,我们了解了读文件写数据库的流程。这里我们假设,日志文件在某一行的数据异常,比如逗号分隔后不是5部分,而变成了6部分,当程序执行到此行时就会抛出 FlatFileParseException 异常,Job停止执行。我们可以从两部分解决这个问题如果我们想让程序忽略这种异常,可以修改Job的配置方式如下:。

以下场景都是基于Spring Batch 3 - 读文件写数据库这篇文件。

忽略异常

有些情况下,我们想让程序忽略掉这种异常,修改Job配置如下:

    
        
            
                
                    
                    
                        
                    
                
            
        
    

skip-limit="2000" 指可以忽略的最大次数(行数),如果你不知道你有都少个异常数据,想全部忽略掉,就尽量配置的大点

Job重新执行

另外的情况,出错时让程序停止,通过手工或其它方式纠正错误后,让程序继续执行。当然,比如我们处理到2051行错误时,纠正错误数据后,我们希望Job能继续从上次失败的行继续开始处理,而不是从第1行从头再来。 这就是Spring Batch的优势,它会自动记录你上次执行成功的地址,再次执行此job时会从这里继续出来。

我们来实际模拟下,假设我们在2051行的数据为:

2016-11-18 13:31:53,/test/user/user_visit/saveVisitStep,gz_qinrong,3,/test/3.201610171_1/Android,/5.1.1/Mi-4c/4faccbe5-bb26-4a0c-94aa-d9e0805f6367

注意在Android后面多加了一个逗号,导致此行逗号分隔后有6部分,而不是正常的5部分

执行job时我们会发生如下异常:

org.springframework.batch.item.file.FlatFileParseException: Parsing error at line: 2051 in resource=[URL [file:/my/access.log]], input=[2016-11-18 13:31:53,/test/user/user_visit/saveVisitStep,gz_qinrong,3,/test/3.201610171_1/Android,/5.1.1/Mi-4c/4faccbe5-bb26-4a0c-94aa-d9e0805f6367]
 at org.springframework.batch.item.file.FlatFileItemReader.doRead(FlatFileItemReader.java:183)
 at org.springframework.batch.item.support.AbstractItemCountingItemStreamItemReader.read(AbstractItemCountingItemStreamItemReader.java:88)
 at org.springframework.batch.core.step.item.SimpleChunkProvider.doRead(SimpleChunkProvider.java:91)
 at org.springframework.batch.core.step.item.SimpleChunkProvider.read(SimpleChunkProvider.java:157)
 at org.springframework.batch.core.step.item.SimpleChunkProvider$1.doInIteration(SimpleChunkProvider.java:116)
 at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:374)
 at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215)
 at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:144)
 at org.springframework.batch.core.step.item.SimpleChunkProvider.provide(SimpleChunkProvider.java:110)
 at org.springframework.batch.core.step.item.ChunkOrientedTasklet.execute(ChunkOrientedTasklet.java:69)
 at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:406)
 at org.springframework.batch.core.step.tasklet.TaskletStep$ChunkTransactionCallback.doInTransaction(TaskletStep.java:330)
 at org.springframework.transaction.support.TransactionTemplate.execute(TransactionTemplate.java:133)
 at org.springframework.batch.core.step.tasklet.TaskletStep$2.doInChunkContext(TaskletStep.java:271)
 at org.springframework.batch.core.scope.context.StepContextRepeatCallback.doInIteration(StepContextRepeatCallback.java:81)
 at org.springframework.batch.repeat.support.RepeatTemplate.getNextResult(RepeatTemplate.java:374)
 at org.springframework.batch.repeat.support.RepeatTemplate.executeInternal(RepeatTemplate.java:215)
 at org.springframework.batch.repeat.support.RepeatTemplate.iterate(RepeatTemplate.java:144)
 at org.springframework.batch.core.step.tasklet.TaskletStep.doExecute(TaskletStep.java:257)
 at org.springframework.batch.core.step.AbstractStep.execute(AbstractStep.java:200)
 at org.springframework.batch.core.job.SimpleStepHandler.handleStep(SimpleStepHandler.java:148)
 at org.springframework.batch.core.job.flow.JobFlowExecutor.executeStep(JobFlowExecutor.java:64)
 at org.springframework.batch.core.job.flow.support.state.StepState.handle(StepState.java:67)
 at org.springframework.batch.core.job.flow.support.SimpleFlow.resume(SimpleFlow.java:169)
 at org.springframework.batch.core.job.flow.support.SimpleFlow.start(SimpleFlow.java:144)
 at org.springframework.batch.core.job.flow.FlowJob.doExecute(FlowJob.java:134)
 at org.springframework.batch.core.job.AbstractJob.execute(AbstractJob.java:306)
 at org.springframework.batch.core.launch.support.SimpleJobLauncher$1.run(SimpleJobLauncher.java:135)
 at org.springframework.core.task.SyncTaskExecutor.execute(SyncTaskExecutor.java:50)
 at org.springframework.batch.core.launch.support.SimpleJobLauncher.run(SimpleJobLauncher.java:128)
 at com.me.springbatch.App.run(App.java:27)
 at com.me.springbatch.file2db.File2DBMain.main(File2DBMain.java:9)
Caused by: org.springframework.batch.item.file.transform.IncorrectTokenCountException: Incorrect number of tokens found in record: expected 5 actual 6
 at org.springframework.batch.item.file.transform.AbstractLineTokenizer.tokenize(AbstractLineTokenizer.java:125)
 at org.springframework.batch.item.file.mapping.DefaultLineMapper.mapLine(DefaultLineMapper.java:43)
 at org.springframework.batch.item.file.FlatFileItemReader.doRead(FlatFileItemReader.java:180)
 ... 31 more

Spring Batch 表BATCH_STEP_EXECUTION中的信息如下:

BATCH_STEP_EXECUTION表

从图上可以看出,程序读了2050行,写了2000行,提交了2次(我们设定了 commit-interval="1000")。

如果我们不处理异常数据(错误依旧存在),再次执行下Job,肯定依旧程序会报异常,表BATCH_STEP_EXECUTION中的信息如下

Spring Batch 3 - Job异常处理_第1张图片
BATCH_STEP_EXECUTION表

从表中可以看出。第二行读到50就异常了,写了0行,也就证明了Job是从上次成功的地方(2000行)后开始执行的。

我们手工纠正2051行的数据,去掉多余的逗号,再次执行,直到Job完全执行成功。

Spring Batch 3 - Job异常处理_第2张图片
日志源文件总共508828行
Spring Batch 3 - Job异常处理_第3张图片
BATCH_STEP_EXECUTION表
业务表

我们可以看到,业务表中的总数据量正好=多次执行的write_count合计。日志文件中的数据被完全处理,且未有重复数据。

总结

  1. 执行时要把spring-context.xml中的以下配置注释掉,否则每次执行都重新drop并创建表,也就无法保留上次执行的job信息了。
    
  1. 两次执行Job时,JobParameters参数必须要一样,否则Spring Batch会认为两次执行的为不同Job。
  2. 从这里我们就能看出Spring Batch的灵活与强大,后面的章节我们再看看Spring Batch还有哪些优点

附完整代码

https://git.oschina.net/heichong/spring-batch-demo

你可能感兴趣的:(Spring Batch 3 - Job异常处理)