【问题标题】:Spring Batch Step does not executeSpring Batch Step 不执行
【发布时间】:2016-08-31 11:38:25
【问题描述】:

我正在尝试解决 Spring Batch 中最近一直困扰我们系统的问题。我们有一份工作,在大多数情况下工作得很好。这是一个下载和处理数据的多步骤作业。

问题是有时这项工作会失败。也许我们尝试连接的服务器会抛出错误,或者我们在工作过程中关闭了服务器。此时,我们的quartz 调度程序下次尝试运行该作业时它似乎没有做任何事情。以下是该职位定义的精简版:

<batch:job id="job.download-stuff" restartable="true">
<batch:validator ref="downloadValidator"/>
<batch:step id="job.download-stuff.download">
    <batch:tasklet ref="salesChannelOrderDownloader" transaction-manager="transactionManager">
        <batch:transaction-attributes isolation="READ_UNCOMMITTED" propagation="NOT_SUPPORTED"/>
        <batch:listeners>
            <batch:listener ref="downloadListener"/>
            <batch:listener ref="loggingContextStepListener" />
        </batch:listeners>
    </batch:tasklet>
    <batch:next on="CONTINUE" to="job.download-stuff.process-stuff.step" />
    <batch:end on="*" />
</batch:step>
<batch:step id="job.download-stuff.process-stuff.step">
    ...
</batch:step>
<batch:listeners>
    <batch:listener ref="loggingContextJobListener"/>
</batch:listeners>

一旦进入此状态,downloadValidator 就会运行,但它永远不会进入第一步 download-stuff.download。我在 tasklet 中设置了一个断点,但它从来没有进入过它。

如果我清除所有存储在我们的 mysql 数据库中的 spring 批处理表,然后重新启动服务器,它将再次开始工作,但我宁愿了解是什么阻止了它此时无法正常运行而不是采用焦土战术来让工作顺利进行。

我是 Spring Batch 的新手,委婉地说,如果我遗漏了重要的细节,请原谅我。我已经设置了断点并打开了日志记录以了解我能做什么。

到目前为止,我通过数据库观察到,条目似乎不再写入 BATCH_STEP_EXECUTION 和 BATCH_JOB_EXECUTION 表。

没有未处于 COMPLETED 状态的作业的 BATCH_JOB_EXECUTION 条目,也没有未处于 COMPLETED 状态的 BATCH_STEP_EXECUTION 条目

您会看到定义了一个 batch:validator,我已经确认 spring 批处理调用了该验证器并且它成功通过(设置断点并逐步通过)。第一步没有执行。

loggingContextJobListener 和 loggingContextStepListener 似乎都不会触发。这可能是什么原因造成的?

更新 我仔细查看了作为批处理添加的downloadListener:侦听器。这是afterStep的源代码:

@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public ExitStatus afterStep(StepExecution stepExecution) {
    long runSeconds = TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - nanoStart);

    // If Success - we're good
    if (stepExecution.getStatus() == BatchStatus.COMPLETED) {
        Long endTs = stepExecution.getExecutionContext().getLong("toTime");
        Date toTime = new Date(endTs);
        handleSuccess(toTime, stepExecution.getWriteCount());
        return null;
    }

    // Otherwise - record errors
    List<Throwable> failures = stepExecution.getFailureExceptions();
    handleError(failures);
    return ExitStatus.FAILED;
}

我确认return ExitStatus.FAILED 行已执行,并且抛出的异常记录在 failureExceptions 中。一旦发生这种情况,BATCH_JOB_EXECUTION 条目似乎处于 COMPLETED 状态(和 exit_code)并且 STEP_EXECUTION 失败。

此时,BATCH_JOB_EXECUTION_PARAMS 表中的条目仍然存在。我实际上尝试修改他们的 KEY_NAME 和 value 列的值,但这仍然不允许作业运行。 只要有与 JOB_EXECUTION_ID 关联的参数,属于同一 BATCH_JOB_INSTANCE 的另一个作业就无法运行。

一旦我删除了 BATCH_JOB_EXECUTION_PARAMS 中针对特定 JOB_EXECUTION_ID 的条目,另一个 BATCH_JOB_EXECUTION 可以运行,即使所有 BATCH_JOB_EXECUTION 条目都处于完成状态。

所以我想我有两个问题 - 这是正确的行为吗?如果是这样,是什么阻止了 BATCH_JOB_EXECUTION_PARAMS 被删除,我该如何删除它们?

【问题讨论】:

    标签: spring spring-batch


    【解决方案1】:

    JobParametersValidator,在您的情况下,downloadValidator bean 在作业开始之前运行。

    在您的情况下发生的情况是您传递作业的参数与“炸毁”JobInstance 的参数相同。但是,由于该作业以戏剧性的方式失败了,它可能没有被置于失败状态。

    您可以使用不同的参数运行作业(以获取新的作业实例),或者在重新启动之前尝试在 BATCH_STEP_EXECUTION 或 BATCH_JOB_EXECUTION 中将前一个步骤/作业的状态更新为 FAILED。

    更新(添加到问题的新信息) 你必须小心你的工作流程。是的,您的步骤失败了,但您的上下文文件表明该作业应该在 CONTINUE 以外的任何地方完成 END(完成)。

    <batch:next on="CONTINUE" to="job.download-stuff.process-stuff.step" />
    <batch:end on="*" />
    

    首先,要非常小心以* 结尾。在您的情况下,它会导致您完成 ExitCodeFAILED 的工作(以“成功”)。此外,成功步骤的默认ExitCodeCOMPLETED,而不是CONTINUE,所以要小心。

    <!-- nothing to me indicates you'd get CONTINUE here, so I changed it -->
    <batch:next on="COMPLETED" to="job.download-stuff.process-stuff.step" />
    
    <!-- if you ever have reason to stop here -->
    <batch:end on="END" /> 
    
    <!-- always fail on anything unexpected -->
    <batch:fail on="*" />
    

    【讨论】:

    • 您提到 JobParametersValidator 在“作业开始之前”运行。你是说这不是正确的行为吗?我确实注意到删除 BATCH_JOB_EXECUTION_PARAMS 是让作业在本地再次运行所需要的全部
    • 这是预期的行为。在其他任何事情发生之前评估作业参数的有效性(例如插入到 BATCH_JOB_INSTANCE)。清除 BATCH_JOB_EXECUTION_PARAMS 表可以有效地确保您获得一个新的 Job 实例,因为之前使用的 Params 记录已经消失。
    • 可能值得重温 JobParameters 和 Spring Batch 的可重启性以获得更多概念背后的背景:docs.spring.io/spring-batch/reference/html/…
    • 我做了更多研究并发布了更新。看来,根据您所说的,我应该将 BATCH_JOB_EXECUTION 设置为失败(它当前注册为 COMPLETE)。可以从我的 downloadListener 中做到这一点吗?
    • 顺便说一句,这篇博文帮助我诊断了错误,我发现它是官方文档之外的一个很好的资源:@​​987654322@
    【解决方案2】:

    遇到同样的问题,在测试/调试过程中我保持作业名称和参数相同,请确保您更改作业名称或作业参数以获得不同的 JobExecution

    【讨论】:

    • 是的,就是这样!谢谢!