在 ItemProcessor 中跳过异常会导致二次行为

时间:2021-04-24 20:54:05

标签: java spring spring-batch

有没有办法改变 ItemProcessor 的跳过行为,使其表现得像 ItemWriter?在 ItemProcessor 中抛出可跳过的异常会导致重新处理所有已接受的项目,从而导致其复杂性为二次的行为。有没有办法像Ite​​mWriter那样改变行为,对所有Item进行回滚,并一个一个处理元素?

工作定义:

@Bean
public Job job() {
    return jobBuilderFactory.get("job").start(step(null)).build();
}

@Bean
public Step step(ReaderProcessorWriter readerProcessorWriter) {
    return stepBuilderFactory.get("step")
            .<Integer, Integer>chunk(20).faultTolerant()
            .skip(RuntimeException.class).skipLimit(10)
            .reader(readerProcessorWriter)
            .processor(readerProcessorWriter)
            .writer(readerProcessorWriter)
            .build();
}

读取器、处理器和写入器:

@Component
public static class ReaderProcessorWriter extends ListItemReader<Integer> implements ItemProcessor<Integer, Integer>, ItemWriter<Integer> {
    private int run;

    public ReaderProcessorWriter() {
        super(List.of(0, 1, 2, 3, 4, 5, 6, 7,8 ,9));
    }

    @Override
    public Integer process(Integer integer) {
        // Probably some long computation involving lots of DB Reading, lasting minutes at worst
        if (integer >= 5) {
            System.out.println("FAIL: " + integer);
            throw new RuntimeException("Hue hue");
        }
        System.out.println("OK: " + integer);
        return integer;
    }

    @Override
    public void write(List<? extends Integer> list) {
        if (run++ == 0) {
            throw new RuntimeException("Writer");
        }
        System.out.println(list);
    }
}

输出:

OK: 0
OK: 1
OK: 2
OK: 3
OK: 4
FAIL: 5
OK: 0
OK: 1
OK: 2
OK: 3
OK: 4
FAIL: 6
OK: 0
OK: 1
OK: 2
OK: 3
OK: 4
FAIL: 7
OK: 0
OK: 1
OK: 2
OK: 3
OK: 4
FAIL: 8
OK: 0
OK: 1
OK: 2
OK: 3
OK: 4
FAIL: 9
OK: 0
OK: 1
OK: 2
OK: 3
OK: 4
OK: 0
[0]
OK: 1
[1]
OK: 2
[2]
OK: 3
[3]
OK: 4
[4]

在示例中,项目 0-4 在成功写入之前被处理了 6 次(假设 Writer 不会抛出)。

  • 5 次,因为处理器回滚
  • 1 表示处理成功

如果 ItemProcessor 使用与 ItemWriter 相同的跳过策略,那么它们只会被处理 2 次:

  • 处理失败 1 次
  • 1 次单独交易

2 个答案:

答案 0 :(得分:1)

您可以尝试将容错处理器配置为非事务性的。然后将成功处理的item的处理结果缓存起来,以便在回滚的情况下,在再次处理chunk的重试过程中,只从缓存中获取处理结果,而不是再次对成功的item进行重新处理。

@Bean
public Step step(ReaderProcessorWriter readerProcessorWriter) {
    return stepBuilderFactory.get("step")
            .<Integer, Integer>chunk(20).faultTolerant()
            .skip(RuntimeException.class).skipLimit(10)
            .reader(readerProcessorWriter)
            .processor(readerProcessorWriter)
            .processorNonTransactional()
            .writer(readerProcessorWriter)
            .build();
}

您必须查看它是否适合您的用例。当使用 JPA 处理项目时,我觉得在回滚的情况下更安全,应该丢弃在回滚事务中加载和处理的所有实体,最好不要在另一个新事务中重用它们,因为实体将在新交易似乎让我觉得事情更复杂了。

如果重新处理一个项目需要相当长的时间,我会尝试看看处理项目中的瓶颈操作是否可以微调,例如使用缓存来缓存那些扩展操作的结果。

答案 1 :(得分:0)

此问题的一个可能解决方法是以下包装器,它们将抛出的异常推迟到 ItemWriter,后者将失败的块拆分为单个事务。我还是想知道这个问题是否有标准的Spring Batch解决方案:

import java.util.concurrent.Semaphore;

// ...

Semaphore semaphore = new Semaphore(2);

for (int i = 0; i < 10; i++)
{
    Simulation experiment = new Simulation(semaphore);

    // ...

    // Handle the InterruptedException thrown here
    experiment.runWithSemaphore();

    /* Alternative to runWithSemaphore(): acquire the permit and call run().
    semaphore.acquire();
    experiment.run();
    */
}
相关问题