Guava Retryer упрощает повторные попытки

Java задняя часть
Guava Retryer упрощает повторные попытки

Мало знаний, большой вызов! Эта статья участвует в "Необходимые знания для программистов«Творческая деятельность.

предисловие

Xiao Hei столкнулся с проблемой во время разработки: модуль, за который я отвечаю, должен вызывать сторонний сервисный интерфейс для запроса информации, и результат запроса напрямую влияет на обработку последующей бизнес-логики;

Этот интерфейс время от времени отключается из-за проблем с сетью, из-за чего моя бизнес-логика не может продолжать обработку;

Как решить эту проблему? , первой мыслью Сяо Хэя было попробовать еще раз.

Вопрос в том, а если опять не получится? Затем повторите попытку. Мы зацикливаем обработку, например, зацикливаем 5 раз, если все не удается, служба задач недоступна, и вызов завершается.

Что, если я подумаю о 5 звонках с периодом времени? Первый раз 1 секунда, потом 3 секунды, потом 5 секунд?

Сяо Хей обнаружил, что все не так просто, и если вы делаете это сами, то легко наделать ошибок.

Если подумать, то это обычное дело, в интернете должны быть колеса, поищите. Случайно позвольте мне найти его, ха-ха.

Guava Retryer

Это небольшое расширение библиотеки Google Guava, позволяющее создавать настраиваемые стратегии повторных попыток для произвольного вызова функции, например, что-то, что взаимодействует с удаленной службой с непостоянным временем безотказной работы.

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

импортировать зависимости

<dependency>
      <groupId>com.github.rholder</groupId>
      <artifactId>guava-retrying</artifactId>
      <version>2.0.0</version>
</dependency>

быстрый старт

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()) // callable返回null时重试
    .retryIfExceptionOfType(IOException.class) // callable抛出IOException重试
    .retryIfRuntimeException() // callable抛出RuntimeException重试
    .withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 重试3次后停止
    .build();
try {
    retryer.call(callable);
} catch (RetryException e) {
    e.printStackTrace();
} catch (ExecutionException e) {
    e.printStackTrace();
}

существуетCallableизcall()метод возвращает ноль, выдаетIOExceptionилиRuntimeExceptionбудет повторять попытку; остановится после 3 попыток и выдаст сообщение, содержащее информацию о последней неудачной попыткеRetryException; если в методе call() возникнет какое-либо другое исключение, оно будет упаковано и помещено вExecutionExceptionНапомним в .

Экспоненциальный откат

Согласно викиExponential backoffОбъяснение: экспоненциальная компенсация — это алгоритм, который экспоненциально снижает скорость процесса с помощью обратной связи, чтобы постепенно найти подходящую скорость.

В Ethernet этот алгоритм обычно используется для запланированных повторных передач после коллизий. Отложенная повторная передача определяется на основе временного интервала и количества попыток повторной передачи.

существуетcПосле коллизии (например, неудачный запрос) 0 и2^c - 1Случайное значение между как количество слотов.

Для первой коллизии каждый отправитель будет ждать 0 или 1 временной интервал для отправки. И после 2-го столкновения отправитель будет ждать от 0 до 3 (по2^2 -1Расчетные) временные интервалы для отправки. А после 3-го столкновения отправитель будет ждать от 0 до 7 (по2^3 - 1Расчетные) временные интервалы для отправки. И так далее...

Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
        .retryIfExceptionOfType(IOException.class)
        .retryIfRuntimeException()
        .withWaitStrategy(WaitStrategies.exponentialWait(100, 5, TimeUnit.MINUTES)) // 指数退避
        .withStopStrategy(StopStrategies.neverStop()) // 永远不停止重试
        .build();

Создайте повторную попытку, которая повторяется бесконечно, увеличиваясь с экспоненциальным интервалом отсрочки после каждой неудачной попытки, максимум до 5 минут. Через 5 минут повторяйте попытку каждые 5 минут.

Откат Фибоначчи

Последовательность ФибоначчиОтносится к такой последовательности:

0,1,1,2,3,5,8,13,21,34,55,89...

Последовательность начинается с термина 3, и каждый член равен сумме двух предыдущих.

Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
        .retryIfExceptionOfType(IOException.class)
        .retryIfRuntimeException()
        .withWaitStrategy(WaitStrategies.fibonacciWait(100, 2, TimeUnit.MINUTES)) // 斐波那契退避
        .withStopStrategy(StopStrategies.neverStop())
        .build();

Создайте повторную попытку, которая повторяется бесконечно, ожидая с шагом интервала отсрочки Фибоначчи после каждой неудачной повторной попытки, максимум до 2 минут. Через 2 минуты повторяйте попытку каждые 2 минуты.

Подобно стратегии экспоненциальной отсрочки, стратегия отсрочки Фибоначчи следует схеме ожидания все дольше и дольше после каждой неудачной попытки.

Для проверки эффективности этих двух стратегий Университет Лидса в Соединенном Королевстве провел тест производительности.По сравнению со стратегией экспоненциального отката, стратегия отсрочки Фибоначчи может иметь лучшую производительность и лучшую пропускную способность.

повторите прослушиватель

Когда происходит повторная попытка, если вам нужно выполнить некоторые дополнительные действия, такие как отправка уведомления по электронной почте, вы можете передатьRetryListener, Guava Retryer автоматически перезванивает слушателю после каждой повторной попытки и поддерживает регистрацию нескольких слушателей.

@Slf4j
class DiyRetryListener<Boolean> implements RetryListener {
    @Override
    public <Boolean> void onRetry(Attempt<Boolean> attempt) {
        log.info("重试次数:{}",attempt.getAttemptNumber());
        log.info("距离第一次重试的延迟:{}",attempt.getDelaySinceFirstAttempt());
        if(attempt.hasException()){
            log.error("异常原因:",attempt.getExceptionCause());
        }else {
            System.out.println("正常处理结果:{}" + attempt.getResult());
        }
    }
}

После определения слушателя его необходимо зарегистрировать в Retryer.

        Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
                .retryIfResult(Predicates.<Boolean>isNull()) // callable返回null时重试
                .retryIfExceptionOfType(IOException.class) // callable抛出IOException重试
                .retryIfRuntimeException() // callable抛出RuntimeException重试
                .withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 重试3次后停止
                .withRetryListener(new DiyRetryListener<Boolean>()) // 注册监听器
                .build();

резюме

Guava Retryer не только поддерживает различные стратегии повторных попыток, но и помещает обработку бизнес-логики вCallable, отдельно от логики обработки повторных попыток для достижения развязки, которая намного лучше, чем собственная обработка цикла записи Сяо Хэя, Guava действительно мощная.