ItemWriter 的 Spring Batch 跳过异常

Posted

技术标签:

【中文标题】ItemWriter 的 Spring Batch 跳过异常【英文标题】:Spring Batch skip exception for ItemWriter 【发布时间】:2014-04-26 16:34:41 【问题描述】:

我正在尝试将 Spring Batch 2.2.5 与 Java 配置一起使用。这是我的配置:

@Configuration
@EnableBatchProcessing
public class JobConfiguration 
    @Autowired
    private JobBuilderFactory jobBuilder;

    @Autowired
    private StepBuilderFactory stepBuilder;

    @Bean
    @Autowired
    public Job processDocumentsJob() 
        return jobBuilder.get("processDocumentsJob")
                .start(procesingStep())
                .build();
    

    @Bean
    @Autowired
    public Step procesingStep() 
           CompositeItemProcessor<File, DocumentPackageFileMetadata> compositeProcessor = new CompositeItemProcessor<File, DocumentPackageFileMetadata>();
           compositeProcessor.setDelegates(Lists.newArrayList(
                   documentPackageFileValidationProcessor(),     
                   documentMetadataFileTransformer()
           ));
        return stepBuilder.get("procesingStep")
                .<File, DocumentPackageFileMetadata>chunk(1)
                .reader(documentPackageFileReader())
                .processor(compositeProcessor)
                .writer(documentMetadataFileMigrator())
                .faultTolerant()
                .skip(DocumentImportException.class)
                .skipLimit(10)
                .listener(stepExecutionListener())
                .build();
    
....



使用上面的配置,如果 itemwriter(documentMetadataFileMigrator 指向的 bean)抛出 'DocumentImportException',则不会跳过该异常。 Spring Batch 实际上会再次重试相同的输入。即它将对“documentPackageFileValidationProcessor”使用相同的输入。

但是,如果我将 itemwriter 内部的逻辑移动到 itemprocessor 中:

 @Bean
        @Autowired
        public Step procesingStep() 
               CompositeItemProcessor<File, DocumentPackageFileMetadata> compositeProcessor = new CompositeItemProcessor<File, DocumentPackageFileMetadata>();
               compositeProcessor.setDelegates(Lists.newArrayList(
                       documentPackageFileValidationProcessor(),     
                       documentMetadataFileTransformer(),
                       documentMetadataFileMigratorAsProcessor() // same as itemwriter, but implemented as itemprocessor
               ));
            return stepBuilder.get("procesingStep")
                    .<File, DocumentPackageFileMetadata>chunk(1)
                    .reader(documentPackageFileReader())
                    .processor(compositeProcessor)                    
                    .faultTolerant()
                    .skip(DocumentImportException.class)
                    .skipLimit(10)
                    .listener(stepExecutionListener())
                    .build();
        

那么异常将被正确跳过。即 Spring Batch 不会针对“documentPackageFileValidationProcessor”重试相同的项目。它将转到下一个要处理的项目(从“documentPackageFileReader”返回的项目)。

这是 Spring Batch 上的错误,还是按预期运行?如果是这样,有人可以指点我相关的文档吗?

谢谢大家,如果这是一个基本问题,我们深表歉意。

最好的问候,

亚历克斯

【问题讨论】:

【参考方案1】:

这种行为是正确的。 ItemWriter 接收要写入的项目列表。如果抛出可跳过的异常,Spring Batch 会尝试确定哪个项目实际导致了异常,因此仅跳过该项目。这样做的方法是回滚事务,将提交间隔更改为 1,然后重新处理每个项目并再次尝试写入。这仅允许跳过有错误的项目,而不需要跳过整个块。

这里讨论了同样的问题(仅使用 XML 配置):How is the skipping implemented in Spring Batch?

【讨论】:

嗨,迈克尔。谢谢回复。我看到的情况如下:假设有 3 项,第 1 项会导致写入失败。由于异常,第一次写入将停止,当然,其余项目不会被写入。然后 Spring 尝试再次写入(如您所说),但包含所有项目(包括项目 1)。这将再次导致失败。就是这样。没有尝试写第 2 项和第 3 项 我刚刚再次浏览了该应用程序。我现在知道为什么它没有处理其余的项目。这是因为在重试期间,其中一个 itemprocessor 抛出了一个不可跳过的异常。傻我!感谢伟大的指针,迈克尔 其实……再想一想——如果块大小为1,那么它不应该需要返回并再次重试整个链来确定哪个项目导致错误,对吧?我的意思是...如果块大小为 1,并且配置是跳过异常,则不重试该异常...然后继续处理下一项。 ItemWriter#write 方法接收项目列表。如果不一次遍历它们,我们就无法确定列表中的哪个在编写器中引发了异常。框架所知道的就是该列表中的某些内容导致某些内容中断。 对我来说,它的行为有点不同。说,我有 4 条记录要读取 - 处理 - 写入,块大小为 10。Record-2 有一些问题,它在“进程”状态期间引发可跳过的异常。它正确地跳过它,但只写入 Record-3 和 Record-4。被正确读取和处理的 Record-1 根本没有被写入。有什么问题?【参考方案2】:

最后,这对我有用 - 如果我想使用 itemwriter,而不需要对同一项目进行重新处理:

 @Bean
    @Autowired
    public Step procesingStep() 
           CompositeItemProcessor<DocumentPackageFileMetadata, DocumentPackageFileMetadata> compositeProcessor = new CompositeItemProcessor<DocumentPackageFileMetadata, DocumentPackageFileMetadata>();
           compositeProcessor.setDelegates(Lists.newArrayList(
                   documentPackageFileValidationProcessor(),
                   documentPackageFileExtractionProcessor(),
                   documentMetadataFileTransformer()
           ));
        return stepBuilder.get("procesingStep")
                .<DocumentPackageFileMetadata, DocumentPackageFileMetadata>chunk(1)
                .reader(documentPackageFileReader())
                .processor(compositeProcessor)
                .writer(documentMetadataFileMigrator())
                .faultTolerant()
                .skip(DocumentImportException.class)
                .noRetry(DocumentImportException.class)
                .noRollback(DocumentImportException.class)
                .skipLimit(10)
                .listener(skipListener())
                .listener(documentPackageReadyForProcessingListener())
                .listener(stepExecutionListener())
                .build();
    

请注意,我已指定“noRetry”和“noRollback”。

【讨论】:

不适合我,它仍然会再次重试该块,即使使用 noRetry 和 noRollBack,也会导致同一记录出现 2 个错误日志。 @CristianoFontes 我知道这已经有一段时间了,但你找到解决办法了吗?

以上是关于ItemWriter 的 Spring Batch 跳过异常的主要内容,如果未能解决你的问题,请参考以下文章

Spring Batch @EnableBatchIntegration 注解

Spring Batch 小任务(Tasklet)步骤

Spring Batch:根据 itemReader 中的结果执行作业

Spring Batch 中的跳过是如何实现的?

Spring-Batch学习总结——重要概念,环境搭建,名词解释,第一个项目及异常处理

Spring Batch - 如何使用一个读取其他步骤的作者的并行步骤?