Перезапустите шаг (или задание) после истечения времени ожидания - PullRequest
0 голосов
/ 07 марта 2019

Возможно ли перезапустить задание или шаг автоматически по истечении времени ожидания? Я попытался повторить попытку и пропустить (пропустить, потому что задание перезапускается каждые 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 , но я не получил удовлетворительных результатов. Мне просто нужно перезапустить этот шаг (или всю работу), когда происходит тайм-аут.

UPDATE

Я тоже это пробовал, но безуспешно: Пружинная партия: повторите задание, если оно не выполнено в определенное время

1 Ответ

1 голос
/ 08 марта 2019

Функции повтора / пропуска применимы к элементам в чанке на отказоустойчивом этапе, ориентированном на чанк, а не на уровне шага или уровне задания. На самом деле в вашем требовании есть две разные вещи:

1. Как остановить работу после истечения заданного времени ожидания?

Помимо внешнего вызова JobOperator#stop после истечения времени ожидания, вы можете остановить работу изнутри самой работы, отправив сигнал остановки через флаг StepExecution#isTerminateOnly. Идея состоит в том, чтобы иметь доступ к выполнению шага, чтобы установить этот флаг после определенного времени ожидания. Это зависит от типа тасклета шага:

Простой тасклет

Для простого тасклета вы можете получить доступ к выполнению шага через 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
    }
}

Этот тасклет будет регулярно проверять, превышено ли время ожидания, и соответственно останавливать шаг (и, следовательно, окружающее задание).

Тасклет-ориентированный тасклет

В этом случае вы можете использовать пошаговый слушатель и установить флаг 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: Повторите задание, если оно не выполнено в определенное время. также является опцией, однако оно оставит вашу работу в состоянии 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;
    });
}

Это автоматически перезапустит задание не более 3 раз, если оно завершится со статусом STOPPED (например, из-за тайм-аута, как показано ранее).

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...