【问题标题】:Spring Batch skip exception for ItemWriterItemWriter 的 Spring Batch 跳过异常
【发布时间】: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 上的错误,还是按预期运行?如果是这样,有人可以指点我相关的文档吗?

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

最好的问候,

亚历克斯

【问题讨论】:

    标签: spring spring-boot 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 我知道这已经有一段时间了,但你找到解决办法了吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-03-18
    • 2020-04-04
    • 1970-01-01
    • 1970-01-01
    • 2021-07-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多