【问题标题】:How to prevent spring batch to start new instance of job?如何防止春季批处理启动新的作业实例?
【发布时间】:2020-06-01 15:01:25
【问题描述】:

如果已经有相同的作业正在运行,建议不要开始作业。

JobExplorer 被简单地注入到计划运行方法的类中

public class JobClass {

    private final Job job;
    private final JobExplorer jobExplorer;
    private final JobLauncher jobLauncher;

    public JobMain(Job job,
                   JobLauncher jobLauncher,
                   JobExplorer jobExplorer) {
        this.job = job;
        this.jobLauncher = jobLauncher;
        this.jobExplorer = jobExplorer;
    }

然后执行

    @Scheduled("0 */5 * ? * *")
    public void startJob() {
        JobParameters jobParameters = new JobParametersBuilder()
                    .addString("jobName", String.valueOf(instant.toEpochMilli()))
                    .toJobParameters();
        jobLauncher.run(job, jobParameters);
    }

这不是解决方案,因为如果 JVM 在作业运行时停止,这将与当前作业运行相同:

jobExplorer.findRunningJobExecutions("jobName")

它将找到所有带有 exitCode ExitStatus.UNKNOWN 的作业。

我认为有 3 个解决方案:

解决方案 1:

停止之前运行的未完成作业并运行新作业 优点:一切都很干净,只有一个属性

CONT:失去当前作业的当前执行

@Scheduled("0 */5 * ? * *")
public void startJob() {
    (JobExecution jobExecution: jobExplorer.findRunningJobExecutions("jobName")) jobExecution.stop();
    ...
}

解决方案 2

计算最近运行的作业之间的时间,如这里所述,如果有,请不要开始新作业: https://stackoverflow.com/a/23218986/1182625

优点:一切都很干净

CONT:必须有双倍属性(5 *60*1000 和 "0 */5 * ? * *")

Set<JobExecution> jobExecutions = jobExplorer.findRunningJobExecutions("jobName");
        if(jobExecutions.size()>1){
            Long currentTime = (new Date()).getTime();
            for(JobExecution execution : jobExecutions) {
                if((currentTime - execution.getStartTime().getTime()) < 5*60*1000) {
                    return;
                } else {
                    execution.stop();
                }
            }

        }

解决方案 3

添加静态(在类实例之间共享)易失(在线程之间共享)标志很简单,这将指示当前正在运行的任何作业 优点:只有一个属性

CONT:需要 2 个侦听器和 volatile 静态变量,我不知道它在多节点环境中的反应如何

    private static volatile boolean FINISHED = true;

然后简单的添加监听器和FINISHED修改方法:

// reset FINISHED after job is done
     @AfterJob
     public void afterJob() {
         FINISHED = true;
     }

    public void setFinished() {
        this.FINISHED = true;
    }

简单的加法:

    @Scheduled("0 */5 * ? * *")
    public void startJob() {
    if(!FINISHED) return;
    FINISHED = false;
    ...
    }

最后添加 StepListener

public MyStepListener() {
...
    @AfterStep
    public ExitStatus afterStep(StepExecution stepExecution) {
    if(stepExecution.getExitStatus().getExitCode().equalsIgnoreCase(ExitStatus.FAILED.getExitCode()))  (new JobMain()).setFinished();
    return null;
    }

【问题讨论】:

    标签: spring-batch


    【解决方案1】:

    好的,我想我可以用 KISS 来做一些事情。

    保持简单和愚蠢。

    因此,如果您想使用属性文件中的值,只需将 fixedDelay 或 fixedStringDelay 放在 @Scheduled 注释中即可轻松实现。

    @Scheduled(initialDelay = 3*60*1000, fixedDelayString ="${job.fixed_delay}")
    

    有了这个,我实现了我没有超过 1 个同一工作的实例。 我只是在准确的时间开始失去那份工作(比如午夜广告或......)

    【讨论】:

      【解决方案2】:

      如果已经有相同的作业正在运行,建议不要开始作业。

      根据设计,Spring Batch 会阻止这种情况。如果您尝试在执行作业时启动同一个作业实例,您将收到JobExecutionAlreadyRunningException

      【讨论】:

      • 好吧,也许我不清楚。这是同一工作的新实例。如果以前的工作没有完成,我不想要相同工作的新实例。
      • @Mahmoud,当我使用 jobLauncher.run 启动作业时,如果我在完成之前启动同一个作业,第一个作业会在未完成的情况下停止运行并重新开始。如何防止停止未完成的工作?
      • @IsaacGS 我不明白这怎么可能,作业启动器不会停止作业,它只会启动它们。请使用示例打开一个单独的问题,我会看一下。
      • @Mahmoud question 提前致谢。
      猜你喜欢
      • 1970-01-01
      • 2020-04-23
      • 1970-01-01
      • 2017-10-06
      • 1970-01-01
      • 2019-01-28
      • 1970-01-01
      • 1970-01-01
      • 2018-12-08
      相关资源
      最近更新 更多