首页 文章

Spring Batch - 重复的STEP消息

提问于
浏览
5

我有一个 spring 批处理作业,预计将根据FIFO顺序处理'N'作业ID . 这个 Spring 季批处理作业有5个步骤 .
我们使用DECIDER来确定是否存在任何job-id . 如果是,请转到第一步并运行该job-id的所有步骤 .
我在spring-batch发出的日志中看到"duplicate step"消息,除非第一个作业中的步骤(比如job-id = 1)获得UNKNOWN状态,否则看起来很好 . 在这种情况下,第二个作业(job-id = 2)的相同步骤无法开始说明"Step is in UNKNOWN state, it is dangerous to restart...." . 有没有更好的方法来定义弹出批处理作业来处理'N' job-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 回答

  • 2

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

    我使用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();
    }
    

    修复是第一个 - >开始

    @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配置 .

  • 0

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

    请参阅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工厂内的流构建器需要提前知道步骤名称以构建转换状态,所以我们"hide"在那一点上的未知步骤在另一步骤中命名 . 也许有办法简化它 . 第一轮的执行是:

    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
    

相关问题