Spring Batch 在集群环境中正确重启未完成的作业

Posted

技术标签:

【中文标题】Spring Batch 在集群环境中正确重启未完成的作业【英文标题】:Spring Batch correctly restart uncompleted jobs in clustered environment 【发布时间】:2019-01-05 05:02:23 【问题描述】:

我使用以下逻辑在单节点 Spring Batch 应用程序上重新启动未完成的作业:

public void restartUncompletedJobs() 

    try 
        jobRegistry.register(new ReferenceJobFactory(documetPipelineJob));

        List<String> jobs = jobExplorer.getJobNames();
        for (String job : jobs) 
            Set<JobExecution> runningJobs = jobExplorer.findRunningJobExecutions(job);

            for (JobExecution runningJob : runningJobs) 
                runningJob.setStatus(BatchStatus.FAILED);
                runningJob.setEndTime(new Date());
                jobRepository.update(runningJob);
                jobOperator.restart(runningJob.getId());
            
        
     catch (Exception e) 
        LOGGER.error(e.getMessage(), e);
    

现在我正试图让它在两节点集群上工作。每个节点上的两个应用程序都将指向共享的 PostgreSQL 数据库。

让我们考虑以下示例:我有 2 个作业实例 - jobInstance1 现在正在 node1 上运行,jobInstance2 正在 node2 上运行。 Node1jobInstance1 执行期间由于某种原因重新启动。在node1重新启动后,spring批处理应用程序尝试使用上面给出的逻辑重新启动未完成的作业-它看到有2个未完成的作业实例-jobInstance1jobInstance2(在node2上正确运行)并尝试重新启动它们。这样可以重新启动唯一的jobInstance1 - 它将同时重新启动jobInstance1jobInstance2.. 但不应重新启动jobInstance2,因为它现在正在node2 上正确执行。

如何在应用启动过程中正确重启未完成的作业(前一次应用终止前),防止jobInstance2等作业也被重启的情况?

更新

这是以下答案中提供的解决方案:

Get the job instances of your job with JobOperator#getJobInstances

For each instance, check if there is a running execution using JobOperator#getExecutions.

2.1 If there is a running execution, move to next instance (in order to let the execution finish either successfully or with a failure)

2.2 If there is no currently running execution, check the status of the last execution and restart it if failed using JobOperator#restart.

我有一个关于 #2.1 的问题 - Spring Batch 会在应用程序重新启动后自动重新启动未完成的作业并运行执行,还是我需要执行手动操作才能这样做?

【问题讨论】:

【参考方案1】:

您的逻辑不是重新启动未完成的作业。您的逻辑正在执行当前正在运行的作业,将其状态设置为 FAILED 并重新启动它们。您的逻辑不应该找到 running 执行,它应该寻找 not 当前正在运行的执行,尤其是失败的执行并重新启动它们。

如何正确重启失败的job,防止jobInstance2等job也重启的情况?

在伪代码中,你需要做的是:

    使用JobOperator#getJobInstances 获取您的作业的作业实例

    对于每个实例,使用JobOperator#getExecutions 检查是否有正在运行的执行。

    2.1 如果有一个正在运行的执行,移动到下一个实例(为了让执行成功或失败)

    2.2 如果当前没有正在运行的执行,检查上次执行的状态,如果失败则使用JobOperator#restart重新启动。

在你的场景中:

jobInstance1 应在步骤 2.2 中重新启动 jobInstance2 应该在步骤 2.1 中过滤,因为它在节点 2 上正在运行。

【讨论】:

感谢您的回答!我有一个问题 - JobOperator#getExecutions 是否会区分现在真正正在运行的执行与那些在数据库中处于运行状态但由于例如应用程序异常终止而当前未运行的执行? 不,JobOperator#getExecutions 返回所有执行,无论其状态如何。还有另一种称为JobOperator#getRunningExecutions 的方法返回正在运行的执行。获得所有执行后,您可以检查它们的状态(并过滤您的案例中正在运行的那些)。 请参考:***.com/questions/32472466/…

以上是关于Spring Batch 在集群环境中正确重启未完成的作业的主要内容,如果未能解决你的问题,请参考以下文章

无法打开spring-batch-core

DB重启后需要重新部署Spring Batch应用程序

Spring Batch @Primary 注释覆盖不正确的bean

Spring-Batch学习总结——重要概念,环境搭建,名词解释,第一个项目及异常处理

Spring Batch批处理框架初探

spring batch简单使用(基于xml)