我有一个弹簧批处理作业,预计会根据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要处理)时,如上所述我们遇到异常,如上所示得到了异常消息。
答案 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。