超时后重新启动步骤(或工作)

时间:2019-03-07 17:13:13

标签: spring-batch

是否可能在发生超时时自动重新启动作业或自动执行步骤? 我尝试了 retry skip (跳过,因为作业每30分钟重新运行一次,前提是没有发生错误),就像这样:

<step id="jobTest.step1">
  <tasklet>
    <transaction-attributes timeout="120"/>
    <chunk reader="testReader" processor="testProcessor" writer="testWriter" commit-interval="10"  retry-limit="3" >
      <retryable-exception-classes>
        <include class="org.springframework.transaction.TransactionTimedOutException"/>
       </retryable-exception-classes>
    </chunk>
    <listeners>
      <listener ref="stepListener" />
    </listeners>
  </tasklet>
</step>

我也尝试了 skip-policy ,但没有得到满意的结果。 发生超时时,我只需要重新启动此步骤(或整个作业)即可。

更新

我也尝试过,但是没有成功: Spring batch: Retry job if does not complete in particular time

1 个答案:

答案 0 :(得分:1)

重试/跳过功能适用于面向容错的面向块的步骤中的块中的项目,而不适用于步骤级别或作业级别。您的要求实际上有两件事:

1。给定超时后如何停止工作?

除了在发生超时后从外部调用JobOperator#stop之外,您还可以通过StepExecution#isTerminateOnly标志发送停止信号来从作业本身中停止作业。想法是可以访问步骤执行,以便在特定超时后设置该标志。这取决于步骤的tasklet类型:

简单Tasklet

对于简单的Tasklet,您可以通过ChunkContext访问步骤执行。这是一个示例:

import java.time.Duration;
import java.util.Date;

import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;

public class MyTasklet implements Tasklet {

    private static final int TIMEOUT = 120; // in minutes (can be turned into a configurable field through a constructor)

    @Override
    public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
        if (timeout(chunkContext)) {
            chunkContext.getStepContext().getStepExecution().setTerminateOnly();
        }
        // do some work
        if (moreWork()) {
            return RepeatStatus.CONTINUABLE;
        } else {
            return RepeatStatus.FINISHED;
        }
    }

    private boolean timeout(ChunkContext chunkContext) {
        Date startTime = chunkContext.getStepContext().getStepExecution().getJobExecution().getStartTime();
        Date now = new Date();
        return Duration.between(startTime.toInstant(), now.toInstant()).toMinutes() > TIMEOUT;
    }

    private boolean moreWork() {
        return false; // TODO implement logic
    }
}

此Tasklet将定期检查是否已超过超时时间,并相应地停止该步骤(并因此停止周围的工作)。

面向块的任务包

在这种情况下,您可以使用步骤侦听器并在生命周期方法之一(terminateOnlyafterRead等)中设置afterWrite标志。这是一个示例:

import java.time.Duration;
import java.util.Date;

import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.listener.StepListenerSupport;
import org.springframework.batch.core.scope.context.ChunkContext;

public class StopListener extends StepListenerSupport {

    private static final int TIMEOUT = 120; // in minutes (can be made configurable through constructor)

    private StepExecution stepExecution;

    @Override
    public void beforeStep(StepExecution stepExecution) {
        this.stepExecution = stepExecution;
    }

    @Override
    public void afterChunk(ChunkContext context) { // or afterRead, or afterWrite, etc.
        if (timeout(context)) {
            this.stepExecution.setTerminateOnly();
        }
    }

    private boolean timeout(ChunkContext chunkContext) {
        Date startTime = chunkContext.getStepContext().getStepExecution().getJobExecution().getStartTime();
        Date now = new Date();
        return Duration.between(startTime.toInstant(), now.toInstant()).toMinutes() > TIMEOUT;
    }
}

想法是一样的,您需要定期检查时间并在适当的时候设置标志。

这两种方式都会使您的工作处于STOPPED状态,这是可重新启动的状态。过去,批处理作业是在批处理窗口中执行的,通常的要求是在关闭窗口时(正常地)停止它们。以前的技术是解决方法。

Spring batch: Retry job if does not complete in particular time中的答案也是一个选项,但是它将使您的工作处于FAILED状态(也为可重新启动状态)。但是您说这对您不起作用。

2。超时后如何自动重新启动作业?

现在您知道如何在超时后停止作业,可以在作业启动器周围使用RetryTemplate并在适当的时候重新启动作业。这是一个示例:

public static void main(String[] args) throws Throwable {
    RetryTemplate retryTemplate = new RetryTemplate();
    retryTemplate.setRetryPolicy(new SimpleRetryPolicy(3));

    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyJob.class);
    JobLauncher jobLauncher = applicationContext.getBean(JobLauncher.class);
    Job job = applicationContext.getBean(Job.class);
    JobParameters jobParameters = new JobParametersBuilder()
            .addDate("runtime", new Date())
            .toJobParameters();

    retryTemplate.execute((RetryCallback<JobExecution, Throwable>) retryContext -> {
        JobExecution jobExecution = jobLauncher.run(job, jobParameters);
        if (jobExecution.getExitStatus().getExitCode().equals(ExitStatus.STOPPED.getExitCode())) {
            throw new Exception("Job timeout");
        }
        return jobExecution;
    });
}

如果完成状态为STOPPED(例如,由于前面所示的超时),它将最多自动重新运行该作业3次。

希望这会有所帮助。