有没有办法改变 ItemProcessor 的跳过行为,使其表现得像 ItemWriter?在 ItemProcessor 中抛出可跳过的异常会导致重新处理所有已接受的项目,从而导致其复杂性为二次的行为。有没有办法像ItemWriter那样改变行为,对所有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 不会抛出)。
如果 ItemProcessor 使用与 ItemWriter 相同的跳过策略,那么它们只会被处理 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();
*/
}