Spring Batch - 重复的STEP消息

时间:2014-08-21 11:55:32

标签: spring-batch

我有一个弹簧批处理作业,预计会根据FIFO顺序处理'N'作业ID。这个春季批处理作业有5个步骤。
我们使用DECIDER来确定是否存在任何job-id。如果是,请转到第一步并运行该job-id的所有步骤。
我在spring-batch发出的日志中看到“重复步骤”消息,除非第一个作业中的步骤(比如job-id = 1)变为UNKNOWN状态,否则看起来很好。在这种情况下,第二个作业(job-id = 2)的相同步骤无法开始说明“步骤处于未知状态,重新启动是危险的......”。有没有更好的方法来定义弹簧批处理作业来处理'N'作业ID。

有一张表格可以保存工作信息。每个作业将订单放入订单表。可能需要在同一天处理两个作业。作业可以插入/更新具有相同修订版(具有其他细节差异)的相同订单编号或相同订单编号的不同修订版。批处理程序必须根据作业表中的success_time在FIFO模型中处理这些作业。

假设表结构如下

Job_Id      job_name    success_time
1           job1        2014-09-29 10:00:00
2           job2        2014-09-29 13:00:00

Order_id    order_number    order_revision  order_details   job_id
1           ABC             1               Test1            1
2           XYZ             1               Test2            1
3           ABC             2               Test1-Rev2       2

示例配置如下所示。为简洁起见,我删除了元数据定义并重复使用了读者和作者。

<batch:step id="abstractParentStep" abstract="true">
    <batch:tasklet>
        <batch:chunk commit-interval="100" />
    </batch:tasklet>
</batch:step>

<-- Using same reader and writer to simplify scenario depiction --> 
<batch:job id="OrderProcessingJob">
    <batch:step id="Collect-Statistics-From-Staging-Tables" next="Validate-Order-Mandatory-Fields" parent="abstractParentStep">
        <batch:tasklet>
            <batch:chunk reader="orderReader" writer="orderWriter" />
        </batch:tasklet>
    </batch:step>
    <batch:step id="Validate-Order-Mandatory-Fields" next="Validate-Item-Mandatory-Fields" parent="abstractParentStep">
        <batch:tasklet>
            <batch:chunk reader="orderReader" writer="orderWriter" />
        </batch:tasklet>
    </batch:step>
    <batch:step id="Validate-Item-Mandatory-Fields" next="decision" parent="abstractParentStep">
        <batch:tasklet>
            <batch:chunk reader="orderReader" writer="orderWriter" />
        </batch:tasklet>
    </batch:step>
    <batch:decision id="decision" decider="processMoreJobsDecider">
        <batch:next on="REPEAT" to="Validate-Order-Mandatory-Fields" />
        <batch:end on="COMPLETED" />
    </batch:decision>

</batch:job>

在第一步中,我们将检查需要处理多少个作业(计数)并将其放入ExecutionContext。在决策程序中,我们检查处理的作业总数是否与计数匹配,如果需要处理更多的job_id,则返回REPEAT状态。

当第一个作业的步骤保持在UNKNOWN状态和第二个作业(因为决策者确定还有一个job_id要处理)时,如上所述我们遇到异常,如上所示得到了异常消息。

2 个答案:

答案 0 :(得分:2)

您应该为每个步骤指定一个唯一的名称。如果您使用分区,则会自动为您完成。

请参阅this gist,文件partitionedSimple.groovy(您只需下载文件并运行groovy <filename.groovy>即可运行所有示例)。 在step1中,我们确定随后需要的步骤数(硬编码为3)并将其保存在作业上下文中(首先在步骤上下文中然后我们进行推广)。我们创建了一个分区步骤partitionedStep,它将启动3个步骤。他们的名字是repeatedStep:<partition name>。在分区中,我们还在上下文中放置了一个名为partitionIndex的键,因此我们可以在实现重复步骤的tasklet中检索它。

然后我们运行一个例子,当它处理第2项时我们强制它失败。我们得到这些步骤执行:

Status is: FAILED
Step executions: 
  1: step1 
  2: partitionedStep FAILED
  4: repeatedStep:partition_1 
  5: repeatedStep:partition_2 FAILED
  3: repeatedStep:partition_3 

如果我们然后重新启动此作业并删除错误触发,则只处理第二项:

Status is: COMPLETED
Step executions: 
  6: partitionedStep 
  null: repeatedStep:partition_1 STARTING
  7: repeatedStep:partition_2 
  null: repeatedStep:partition_3 STARTING

我还添加了一个slightly more complicated example,其中重复步骤实际上是一个流程步骤,步骤名称是手动动态生成的 - 如果你想重复一个流程,这很重要,因为你必须为每次执行流程的步骤提供唯一的名称。

这也可以在不使用looping decider进行分区的情况下完成。这里的想法是你有一个包装步骤重复(allowStartIfComplete)并用你想要的步骤包裹流程。由于步骤范围的bean工厂,这些步骤是按需创建的。看似冗余的包装步骤的原因是job() bean工厂内的流构建器需要提前知道步骤名称以构建转换状态,因此我们“隐藏”那个位置的未知步骤名称步。也许有一种简化它的方法。第一轮的执行是:

Step executions: 
  1: step1 
  2: wrappingStep 
  3: repeated-1 
  4: wrappingStep FAILED
  5: repeated-2 FAILED

(通知repeated-3永远不会被执行)

并在第二次运行中:

Step executions: 
  6: wrappingStep 
  7: wrappingStep 
  8: repeated-2 
  9: wrappingStep 
  10: repeated-3

答案 1 :(得分:0)

你的问题是你用'下一个'而不是一个开始来开始你的流程。

我使用Java配置而不是XML,但是得到了类似的异常(不是特别有用的错误输出):

@Bean
public Flow insertGbDatabaseRecordsFlow(final Step populateFpSettlementsStep, final Step populateGbDatabaseStep) {
    FlowBuilder<Flow> flowBuilder = new FlowBuilder<>("insertGbDatabaseRecordsFlow");
    flowBuilder.next(populateFpSettlementsStep);
    flowBuilder.next(populateGbDatabaseStep);
    return flowBuilder.build();
}

修复是第一个 - &gt;启动

@Bean
public Flow insertGbDatabaseRecordsFlow(final Step populateFpSettlementsStep, final Step populateGbDatabaseStep) {
    FlowBuilder<Flow> flowBuilder = new FlowBuilder<>("insertGbDatabaseRecordsFlow");
    flowBuilder.start(populateFpSettlementsStep);
    flowBuilder.next(populateGbDatabaseStep);
    return flowBuilder.build();
}

大概同样适用于Spring Batch xml config。