是否可能在发生超时时自动重新启动作业或自动执行步骤? 我尝试了 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
答案 0 :(得分:1)
重试/跳过功能适用于面向容错的面向块的步骤中的块中的项目,而不适用于步骤级别或作业级别。您的要求实际上有两件事:
除了在发生超时后从外部调用JobOperator#stop
之外,您还可以通过StepExecution#isTerminateOnly
标志发送停止信号来从作业本身中停止作业。想法是可以访问步骤执行,以便在特定超时后设置该标志。这取决于步骤的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将定期检查是否已超过超时时间,并相应地停止该步骤(并因此停止周围的工作)。
在这种情况下,您可以使用步骤侦听器并在生命周期方法之一(terminateOnly
,afterRead
等)中设置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
状态(也为可重新启动状态)。但是您说这对您不起作用。
现在您知道如何在超时后停止作业,可以在作业启动器周围使用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次。
希望这会有所帮助。