Retry Task Framework

У меня есть ряд ситуаций, когда мне нужно повторить задачу n-раз, если она не сработает (иногда с некоторой формой логики возврата к повторной попытке). Как правило, если генерируется исключение, задача должна быть повторена до максимального количества попыток.

Я могу легко написать что-то, чтобы сделать это достаточно в целом, но не желая изобретать колесо, мне было интересно, может ли кто-нибудь рекомендовать какие-либо рамки для этого. Единственное, что я смог найти, это: Ant Повторить, но я не хочу использовать задачи Ant непосредственно в моем приложении.

Спасибо

Ответ 1

Вы можете использовать RetriableTasks, как указано в этом сообщении: Повторная операция в Java. Вы можете легко изменить алгоритм ожидания, если хотите.

Пример кода:

//creates a task which will retry 3 times with an interval of 5 seconds
RetriableTask r = new RetriableTask(3, 5000, new Callable(){
    public Object call() throws Exception{
        //put your code here
    }
});
r.call();

Ответ 2

Проверьте Failsafe. Это простая библиотека с нулевой зависимостью для выполнения повторных попыток; она поддерживает синхронные и асинхронные повторные попытки, интеграцию Java 8, прослушиватели событий, интеграцию с другими асинхронными API-интерфейсами и т. Д.:

RetryPolicy retryPolicy = new RetryPolicy()
  .handle(ConnectException.class, SocketException.class);
  .withMaxRetries(3);

Connection connection = Failsafe.with(retryPolicy).get(() -> connect());

Не становится намного проще.

Ответ 3

Если вы используете Spring:

//import the necessary classes
import org.springframework.batch.retry.RetryCallback;
import org.springframework.batch.retry.RetryContext;
import org.springframework.batch.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.batch.retry.policy.SimpleRetryPolicy;
import org.springframework.batch.retry.support.RetryTemplate;
...

// create the retry template
final RetryTemplate template = new RetryTemplate();
template.setRetryPolicy(new SimpleRetryPolicy(5));
final ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(1000L);
template.setBackOffPolicy(backOffPolicy);

// execute the operation using the retry template
template.execute(new RetryCallback<Remote>() {
  @Override
  public Remote doWithRetry(final RetryContext context) throws Exception {
    return (Remote) Naming.lookup("rmi://somehost:2106/MyApp");
  }
});

Оригинальное сообщение в блоге

Ответ 4

Если вы используете Spring, он очень прост, используя Spring Повторить Библиотека.

Теперь Spring Retry - это отдельная библиотека (ранее, она была частью рамки Spring Пакет).

Шаг1: Добавьте Spring зависимость повтора.

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.1.2.RELEASE</version>
</dependency>

Шаг 2: Добавить @EnableRetry аннотацию к вашему классу, которая содержит метод main() вашего приложения или в любой из ваших классов @Configuration.

Шаг 3: Добавить @Retryable аннотацию к вашему методу, который вы хотите повторить/вызвать снова, в случае исключений.

@Retryable(maxAttempts=5,backoff = @Backoff(delay = 3000))
public void retrySomething() throws Exception{
    logger.info("printSomething{} is called");
    throw new SQLException();
}

Эта аннотация @Retryable будет повторять/вызывать retrySomething() 5 раз ( включая первый сбой).

Текущий поток будет ждать 3000 мс или 3 секунды между следующей попыткой.

Ответ 5

У меня есть один ответ, но это было три года назад, и я должен добавить, что теперь я абсолютно люблю проект guava-retrying. Позвольте мне просто показать вам код.

Callable<Boolean> callable = new Callable<Boolean>() {
    public Boolean call() throws Exception {
        return true; // do something useful here
    }
};

Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
        .retryIfResult(Predicates.<Boolean>isNull())
        .retryIfExceptionOfType(IOException.class)
        .retryIfRuntimeException()
        .withStopStrategy(StopStrategies.stopAfterAttempt(3))
        .build();
try {
    retryer.call(callable);
} catch (RetryException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
}

Ответ 6

Один из вариантов, чтобы исключить это из вашей кодовой базы, - использовать шаблон команды между компонентами вашего приложения. После того, как вы перейдете к бизнес-методу в объект, вы можете легко передать вызов и иметь абстрактный RetryHandler, который принимает команду и повторяет ее. Это должно быть независимым от фактического вызова и повторного использования.

Ответ 7

Вы можете использовать Quartz. Посмотрите этот ответ на переполнение стека.

Ответ 8

Я реализовал довольно гибкую утилиту повтора здесь

Вы можете повторить любую Callable с помощью:

public static <T> T executeWithRetry(final Callable<T> what, final int nrImmediateRetries,
        final int nrTotalRetries, final int retryWaitMillis, final int timeoutMillis,
        final Predicate<? super T> retryOnReturnVal, final Predicate<Exception> retryOnException)

с немедленными + задержанными повторами с максимальным временем ожидания и повторением либо решений, основанных на результате или исключении.

Существует несколько других версий этой функции с большей или меньшей гибкостью.

Я написал также аспект, который может быть применен с аннотациями Retry Retry Aspect