Spring Batch 3 - Job异常处理

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

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

忽略异常

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

    <batch:job id="readFile2DBJob">
        <batch:step id="readFile2DBStep">
            <batch:tasklet>
                <batch:chunk reader="logReader" writer="mysqlItemWriter" commit-interval="1000" skip-limit="2000">
                    <!-- 发生这些异常时,直接跳过继续处理,skippable-exception-classes必须要配合skip-limit属性使用 -->
                    <batch:skippable-exception-classes>
                        <batch:include class="org.springframework.batch.item.file.FlatFileParseException"/>
                    </batch:skippable-exception-classes>
                </batch:chunk>
            </batch:tasklet>
        </batch:step>
    </batch: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中的信息如下

BATCH_STEP_EXECUTION表

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

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

日志源文件总共508828行
BATCH_STEP_EXECUTION表
业务表

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

总结

  1. 执行时要把spring-context.xml中的以下配置注释掉,否则每次执行都重新drop并创建表,也就无法保留上次执行的job信息了。
    <!-- 
       初始化脚本,主要用来创建spring-batch运行时的数据,应用生成环境时应该去掉此配置,手工创建下对应的表 
       spring-batch本身job运行需要的表的创建脚本,spring-batch.jar中默认提供了各种数据库的DDL语句
    
    <jdbc:initialize-database data-source="dataSource">
        <jdbc:script location="${batch.schema.script.drop}" />
        <jdbc:script location="${batch.schema.script}" />
    </jdbc:initialize-database>
-->
  1. 两次执行Job时,JobParameters参数必须要一样,否则Spring Batch会认为两次执行的为不同Job。
  2. 从这里我们就能看出Spring Batch的灵活与强大,后面的章节我们再看看Spring Batch还有哪些优点

附完整代码

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容