如何从 StepExecutionListener afterStep 方法停止 Spring 批处理作业?
How to halt a Spring Batch job from a StepExecutionListener afterStep method?
我已使用此方法从之前的步骤成功停止了作业。
public class FirstListener implements StepExecutionListener {
@Override
public void beforeStep(StepExecution stepExecution) {
boolean shouldRun = shouldJobRun();
if (!shouldRun) {
// listeners will still work, but any other step logic (reader, processor, writer) will not happen
stepExecution.setTerminateOnly();
stepExecution.setExitStatus(new ExitStatus("STOPPED", "Job should not be run right now."));
LOGGER.warn(duplicate_message);
}
}
代码已针对 brevity/clarity 进行了删减,但这就是要点。调用 stepExecution.setTerminateOnly()
和 stepExecution.setExitStatus()
足以让 Spring Batch 停止作业并且不执行任何后续步骤。状态已正确记录在 BATCH_JOB_EXECUTION
表
中
EXIT_MESSAGE STATUS
org.springframework.batch.core.JobInterruptedException STOPPED
但是,afterStep
方法中的相同方法被翻转并无法识别。状态被记录为 COMPLETED 并且所有后续步骤都按照他们的快乐方式进行(最终以他们自己可怕的方式失败,因为 afterStep 中的故障检测正在检测故障,因此他们不必这样做)。
public class SecondListener implements StepExecutionListener {
@Override
public ExitStatus afterStep(StepExecution stepExecution) {
if (stepExecution.getExitStatus().getExitCode().equals(ExitStatus.STOPPED.getExitCode())) {
return stepExecution.getExitStatus();
}
if (everythingIsOkay()) {
return stepExecution.getExitStatus();
}
String failureMessage = "Something bad happened.";
LOGGER.error(failureMessage);
ExitStatus exitStatus = new ExitStatus(ExitStatus.FAILED.getExitCode(), failureMessage);
stepExecution.setExitStatus(exitStatus);
stepExecution.setTerminateOnly();
return exitStatus;
}
这是我能想到的唯一问题:使用复合侦听器,两个侦听器处于同一步骤。
@Bean(name = "org.springframework.batch.core.StepExecutionListener-compositeListener")
@StepScope
public StepExecutionListener compositeListener() {
CompositeStepExecutionListener listener = new CompositeStepExecutionListener();
List<StepExecutionListener> listeners = Lists.newArrayList(secondListener());
if (jobShouldHaveFirstListener()) {
listeners.add(0, firstListener()); // prepend; delegates are called in order
}
listener.setListeners(listeners.toArray());
return listener;
}
public Step firstStep() {
return stepBuilderFactory.get("firstStep")
.listener(compositeListener)
// Small batch size for frequency capping, which happens in the writer, before analytics get written
.<Recipient, Recipient>chunk(500)
.reader(rawRecipientInputFileItemReader)
.processor(recipientItemProcessor)
.writer(recipientWriter)
.throttleLimit(2)
.build();
}
@Bean(name = "org.springframework.batch.core.Job-delivery")
public Job deliveryJob() {
return jobs.get("delivery")
.preventRestart()
.start(firstStep)
.next(deliveryStep)
.next(handleSentStep)
.listener(failedCleanupListener)
.build();
}
我还能做些什么来正确停止执行?
经过多次试验,我发现以下流程定义将允许从 StepListener 正确停止作业,而无需在第一步之后执行步骤。
return jobs.get("delivery")
.preventRestart()
.listener(failedCleanupListener)
.flow(firstStep)
.next(deliveryStep)
.next(handleSentStep)
.end()
.build();
主要区别在于将 start()
更改为 flow()
并添加从 .start
方法返回的 FlowBuilder.end()
method call to the end of the builder chain. The SimpleJobBuilder
class 不公开类似 end()
方法。
我不知道为什么这会在作业执行的内部产生如此大的差异,我很乐意将一些要点奖励给可以阐明 什么 实际差异的人为什么 使用 SimpleJobBuilder 忽略它的步骤执行状态代码。但我发现了一些有用的东西,这就是现在最重要的。
我已使用此方法从之前的步骤成功停止了作业。
public class FirstListener implements StepExecutionListener {
@Override
public void beforeStep(StepExecution stepExecution) {
boolean shouldRun = shouldJobRun();
if (!shouldRun) {
// listeners will still work, but any other step logic (reader, processor, writer) will not happen
stepExecution.setTerminateOnly();
stepExecution.setExitStatus(new ExitStatus("STOPPED", "Job should not be run right now."));
LOGGER.warn(duplicate_message);
}
}
代码已针对 brevity/clarity 进行了删减,但这就是要点。调用 stepExecution.setTerminateOnly()
和 stepExecution.setExitStatus()
足以让 Spring Batch 停止作业并且不执行任何后续步骤。状态已正确记录在 BATCH_JOB_EXECUTION
表
EXIT_MESSAGE STATUS
org.springframework.batch.core.JobInterruptedException STOPPED
但是,afterStep
方法中的相同方法被翻转并无法识别。状态被记录为 COMPLETED 并且所有后续步骤都按照他们的快乐方式进行(最终以他们自己可怕的方式失败,因为 afterStep 中的故障检测正在检测故障,因此他们不必这样做)。
public class SecondListener implements StepExecutionListener {
@Override
public ExitStatus afterStep(StepExecution stepExecution) {
if (stepExecution.getExitStatus().getExitCode().equals(ExitStatus.STOPPED.getExitCode())) {
return stepExecution.getExitStatus();
}
if (everythingIsOkay()) {
return stepExecution.getExitStatus();
}
String failureMessage = "Something bad happened.";
LOGGER.error(failureMessage);
ExitStatus exitStatus = new ExitStatus(ExitStatus.FAILED.getExitCode(), failureMessage);
stepExecution.setExitStatus(exitStatus);
stepExecution.setTerminateOnly();
return exitStatus;
}
这是我能想到的唯一问题:使用复合侦听器,两个侦听器处于同一步骤。
@Bean(name = "org.springframework.batch.core.StepExecutionListener-compositeListener")
@StepScope
public StepExecutionListener compositeListener() {
CompositeStepExecutionListener listener = new CompositeStepExecutionListener();
List<StepExecutionListener> listeners = Lists.newArrayList(secondListener());
if (jobShouldHaveFirstListener()) {
listeners.add(0, firstListener()); // prepend; delegates are called in order
}
listener.setListeners(listeners.toArray());
return listener;
}
public Step firstStep() {
return stepBuilderFactory.get("firstStep")
.listener(compositeListener)
// Small batch size for frequency capping, which happens in the writer, before analytics get written
.<Recipient, Recipient>chunk(500)
.reader(rawRecipientInputFileItemReader)
.processor(recipientItemProcessor)
.writer(recipientWriter)
.throttleLimit(2)
.build();
}
@Bean(name = "org.springframework.batch.core.Job-delivery")
public Job deliveryJob() {
return jobs.get("delivery")
.preventRestart()
.start(firstStep)
.next(deliveryStep)
.next(handleSentStep)
.listener(failedCleanupListener)
.build();
}
我还能做些什么来正确停止执行?
经过多次试验,我发现以下流程定义将允许从 StepListener 正确停止作业,而无需在第一步之后执行步骤。
return jobs.get("delivery")
.preventRestart()
.listener(failedCleanupListener)
.flow(firstStep)
.next(deliveryStep)
.next(handleSentStep)
.end()
.build();
主要区别在于将 start()
更改为 flow()
并添加从 .start
方法返回的 FlowBuilder.end()
method call to the end of the builder chain. The SimpleJobBuilder
class 不公开类似 end()
方法。
我不知道为什么这会在作业执行的内部产生如此大的差异,我很乐意将一些要点奖励给可以阐明 什么 实际差异的人为什么 使用 SimpleJobBuilder 忽略它的步骤执行状态代码。但我发现了一些有用的东西,这就是现在最重要的。