我有一份multi-threaded chunk-oriented step的工作,我需要计算有多少书面项目满足某些业务规则。 (PS:出于遗留原因,我使用的是Spring Batch 3.0.x)
我要记住,如果发生回滚,那么必须忽略同一事务(即同一块)中先前已计算在内的项目。因此,我不能只是直接从Writer更新JobExecutionContext,而是更新ChunkContext中的属性,并使用 CustomChunkListener 仅在块成功后才更新JobExecutionContext(如下面的代码所示)。 / p>
在使该步骤成为多线程之前,我已按照预期的方式执行了以下实现(我尽可能简化了代码,以专注于此问题):
CustomItemWriter
public class CustomItemWriter implements ItemWriter<String[]> {
private ChunkContext chunkContext;
@Override
public void write(List<? extends String[]> items) throws Exception {
for (String[] item : items) {
((AtomicLong)this.chunkContext.getAttribute("chunkCounter")).incrementAndGet();
}
}
@BeforeChunk
private void beforeChunk(ChunkContext chunkContext) {
this.chunkContext = chunkContext;
}
}
CustomChunkListener
public class CustomChunkListener extends ChunkListenerSupport {
@Override
public void beforeChunk(ChunkContext context) {
context.setAttribute("chunkCounter", new AtomicLong());
}
@Override
public void afterChunk(ChunkContext context) {
((AtomicLong)context.getStepContext().getJobExecutionContext().get("jobCounter")).addAndGet(((AtomicLong)context.getAttribute("chunkCounter")).get());
}
}
CustomJobListener
public class CustomJobListener extends JobExecutionListenerSupport {
@Override
public void beforeJob(JobExecution jobExecution) {
jobExecution.getExecutionContext().put("jobCounter", new AtomicLong());
}
@Override
public void afterJob(JobExecution jobExecution) {
System.out.println("jobCounter = " + jobExecution.getExecutionContext().get("jobCounter"));
}
}
但是,当我将作业配置为以多线程方式运行该步骤时,该计数器未正确更新,并且我知道这是由于我在CustomItemWriter中访问ChunkContext的方式所致。
Bean CustomItemWriter具有“步范围”(据我所知,没有“块范围”可用),因此每次线程启动新的ChunkContext时,CustomItemWriter中的beforeChunk方法都会覆盖之前的ChunkContext和搞砸了所有东西(由于我失去了对以前的ChunkContext实例的引用,因此先前的计数将不复存在)。
因此,我设法通过使用ThreadLocal来解决此问题,如下所示:
CustomItemWriter(v2)
public class CustomItemWriter implements ItemWriter<String[]> {
private ThreadLocal<ChunkContext> chunkContext = new ThreadLocal<ChunkContext>();
@Override
public void write(List<? extends String[]> items) throws Exception {
for (String[] item : items) {
((AtomicLong)this.chunkContext.get().getAttribute("chunkCounter")).incrementAndGet();
}
}
@BeforeChunk
private void beforeChunk(ChunkContext chunkContext) {
this.chunkContext.set(chunkContext);
}
}
尽管我已经设法解决了这个问题,但是我想知道是否有更好的方法可以从CustomItemWriter中访问当前的ChunkContext(对于当前线程)。有没有办法以编程方式获取它?要以“春季方式”进行操作,也许应该在较新版本的Spring Batch中实现[新] Chung Scope?
PS:而且,尽管问题已解决,但我认为写这个问题会有所帮助,这样可以帮助有相同需求的人。
答案 0 :(得分:0)
尽管我已经设法解决了这个问题,但我想知道是否有更好的方法可以从CustomItemWriter中访问当前的ChunkContext(对于当前线程)
不,我认为没有其他方法可以做到这一点。但是,不建议在多线程步骤中使用块上下文。此上下文是一个可变状态,在处理该步骤的所有线程之间共享(带有在多线程环境中使用共享可变状态的所有讨厌的错误)。
文档将在这方面进行更新,请参见https://github.com/spring-projects/spring-batch/pull/591/files#diff-177ad333794c9242aaa9ec2d0bec1842R147-R150。