当前位置: 首页 > 知识库问答 >
问题:

如何防止Spring批次启动新的作业实例?

孟树
2023-03-14

想法是,如果已经运行相同的作业,则不要启动作业。

作业资源管理器是简单注入到类中,其中是用于运行的计划方法

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种解决方案:

解决方案一:

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

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

@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

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

CONT:需要2个侦听器,以及不知道在多节点环境中反应如何的易失性静态变量

    private static volatile boolean FINISHED = true;

然后简单添加监听器并完成修改方法:

// 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;
    }

共有2个答案

牟恺
2023-03-14

想法是,如果已经运行相同的作业,则不要启动作业。

根据设计,Spring Batch将阻止这种情况。如果您尝试在同一作业实例正在运行的作业执行时启动它,您将获得一个JobExecutionAlreadyRunningException

逄学潞
2023-03-14

好吧,我想我走得很远,可能是KISS。

保持简单

所以,要实现这一点,如果您想使用属性文件中的值,可以简单地将fixedDelay或fixedStringDelay放在@Scheduled注释中。

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

这样我就实现了在同一时间我不会有一个以上相同工作的实例。我只在特定时间(如午夜或凌晨)丢掉工作...)

 类似资料:
  • 我试图从步骤(实现接口Tasklet的类的execute方法)内部启动作业。 显然我收到了例外 Java语言lang.IllegalStateException:在JobRepository中检测到现有事务 如何使Spring批处理步骤不是事务性的? 有人能解决我从一步内启动工作的主要需求吗? 提前感谢您的帮助!

  • 我有一个spring批处理作业,从CSV文件读取并写入数据库。我想让它重新启动。例如,如果在读取文件或写入db时出现异常,导致作业失败,则应从失败的同一点/块重新开始,而不是从头开始读取整个文件。 我正在从一个endpoint触发作业启动器,并在我的控制器中配置了它。 目前,我正在通过控制器将参数(这是一个唯一的标识符/数字)传递给作业参数,以运行新的作业实例。如果作业失败,我将使用与GET请求中

  • 在我的应用程序中,我有一个显示某种级别的列表视图。 我用资产中的一个文件填充listview。我在资产中的文件如下所示: 我的listview显示级别,当我单击listview的某个项目转到另一个活动时,“转到下一个活动”也会显示我的问题和选项。一切正常,我的问题是,当在第二个活动中按后退按钮转到第一个活动时,第一个活动再次启动,我的onCreate方法再次运行,我的listview项增加。(例如

  • 我遵循了spring批处理文档,无法异步运行我的作业。 因此,我从一个web容器运行该作业,该作业将通过RESTendpoint触发。 我想让JobInstance ID在完成整个作业之前传递它作为响应。因此,他们可以稍后使用JobInstance ID检查作业的状态,而不是等待。但我没能让它工作。下面是我尝试过的示例代码。请让我知道我错过了什么或错了什么。 BatchConfig创建异步JobL

  • 我已经将我的Spring批处理配置为每当通过API调用从UI发出请求时触发作业。我面临的问题是,该作业仅在第一次和其他尝试时工作正常,每当调用时,作业不会以预期的方式响应。似乎他们正在尝试恢复,但我想再次重新启动整个执行。感谢您提前提供的任何帮助。 主要的班 配置班 JobListener.class 我的听众。班 Controller.class 应用属性 首次发出API请求时的响应 其他时间的