JSR 352:如何从分区步骤的每个分区的Writer收集数据?

时间:2016-06-18 10:27:15

标签: jsr352 java-batch

所以,我在一个写入数据库的步骤中有2个分区。我想记录每个分区中写入的行数,得到总和,并将其打印到日志中;

我正在考虑在Writer中使用static变量,并使用Step Context / Job Context在Step Listener的afterStep()中获取它。但是,当我尝试它时,我得到了null。我可以在Reader的close()中获取这些值。

这是正确的方法吗?或者我应该使用分区收集器/减速器/分析器?

我在Websphere Liberty中使用java批处理。我正在Eclipse中开发。

2 个答案:

答案 0 :(得分:2)

  

我正在考虑在Writer中使用静态变量,并使用Step Context / Job Context在Step Listener的afterStep()中获取它。但是,当我尝试它时,我得到了空。

ItemWriter 此时可能已被销毁,但我不确定。

  

这是正确的方法吗?

是的,它应该足够好了。但是,您需要确保为所有分区共享总行数,因为批处理运行时为每个分区维护 StepContext 克隆。您应该使用JobContext

我认为使用 PartitionCollector PartitionAnalyzer 也是一个不错的选择。接口 PartitionCollector 有一个方法collectPartitionData()来收集来自其分区的数据。收集后,批处理运行时将此数据传递给 PartitionAnalyzer 以分析数据。请注意,还有

  • 每个步骤N PartitionCollector(每个分区1个)
  • 每步的N StepContext(每个分区1个)
  • 每步1分区分析

所写的记录可以通过 StepContext ' s transientUserData传递。由于 StepContext 是为自己的步分区保留的,因此临时用户数据不会被其他分区覆盖。

以下是实施:

MyItemWriter

@Inject
private StepContext stepContext;

@Override
public void writeItems(List<Object> items) throws Exception {
    // ...
    Object userData = stepContext.getTransientUserData();
    stepContext.setTransientUserData(partRowCount);
}

<强> MyPartitionCollector

@Inject
private StepContext stepContext;

@Override
public Serializable collectPartitionData() throws Exception {

    // get transient user data
    Object userData = stepContext.getTransientUserData();
    int partRowCount = userData != null ? (int) userData : 0;
    return partRowCount;
}

<强> MyPartitionAnalyzer

private int rowCount = 0;

@Override
public void analyzeCollectorData(Serializable fromCollector) throws Exception {
    rowCount += (int) fromCollector;
    System.out.printf("%d rows processed (all partitions).%n", rowCount);
}

参考:JSR352 v1.0 Final Release.pdf

答案 1 :(得分:2)

让我在接受的答案中提供一些替代方案并添加一些评论。

PartitionAnalyzer变体 - 使用analyzeStatus()方法

另一种技术是使用analyzeStatus,只在每个整个分区的末尾调用,并传递分区级退出状态。

public void analyzeStatus(BatchStatus batchStatus, String exitStatus) 

相反,使用analyzeCollectorData的上述答案会在每个分区的每个块的末尾被调用。

E.g。

public class MyItemWriteListener extends AbstractItemWriteListener {

@Inject
StepContext stepCtx;

@Override
public void afterWrite(List<Object> items) throws Exception {
    // update 'newCount' based on items.size()
    stepCtx.setExitStatus(Integer.toString(newCount));
}

显然,只有当您没有将退出状态用于其他目的时,这才有效。您可以从任何工件设置退出状态(尽管这种自由可能是需要跟踪的另一件事)。

评论

API旨在促进跨JVM调度各个分区的实现(例如,在Liberty中,您可以看到此here。)但是使用 static 将您绑定到单个JVM,所以这不是推荐的方法。

另请注意, JobContext StepContext 都以我们在批处理中看到的“线程局部”方式实现。