Лучший способ реализовать «повторные попытки» при достижении конечной точки api? - PullRequest
0 голосов
/ 16 июня 2020

Я использую ApacheHttpClient

У меня есть метод Java (внутри микросервиса Java), который выполняет запрос Http POST к внешнему конечная точка (конечная точка, которой я не владею). Обычно все работает хорошо, но иногда конечная точка выходит из строя. Код выглядит примерно так (упрощенно):

private HttpResponseData postRequest(String url) throws Exception {
        HttpResponseData response = null;
        try (InputStream key = MyAPICaller.class.getResourceAsStream(keyPath)) {
            MyAPICaller.initializeApiClient(Username, PassString, key);
            int attempts = REQUEST_RETRY_COUNT; // starts at 3

            while (attempts-- > 0) {
                try {
                    response = MyAPICaller.getInstance().post(url);
                    break;
                } catch (Exception e) {
                    log.error("Post Request to {} failed. Retries remaining {}", url, attempts);
                    Thread.sleep(REQUEST_RETRY_DELAY * 1000);
                }
            }

            if (response == null)
                throw new Exception("Post request retries exceeded. Unable to complete request.");

        }
        return response;
    }

Я не писал исходный код, но, как вы можете видеть, похоже, что он делает запрос, а REQUEST_RETRY_COUNT больше 0 (что всегда будет казаться), он попытается сделать сообщение по URL-адресу. Похоже, что по какой-то причине там есть точка останова, поэтому после перехода в блок try он всегда прерывается, и нет механизма повтора.

Есть ли общий шаблон проектирования в Java для реализации шаблона повтора для попадания во внешнюю конечную точку? Я знаю, что в Javascript вы можете использовать Fetch API с возвратом обещания, есть ли что-то подобное с Java?

1 Ответ

0 голосов
/ 16 июня 2020

Облачные платформы, такие как GCP и AWS, обычно имеют собственную стратегию повторных попыток, и это должен быть предпочтительный подход.

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

Это может быть основано на аннотации, где вы аннотируете свои клиентские методы. Например, вы аннотируете свой метод API следующим образом:

@ Retry (maxTries = 3, retryOnExceptions = {RpcException.class}) publi c UserInfo getUserInfo (String userId);

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Retry {

  int maxTries() default 0;

  /**
   * Attempt retry if one of the following exceptions is thrown.
   * @return array of applicable exceptions
   */
  Class<? extends Throwable> [] retryOnExceptions() default {Throwable.class};
}

Перехватчик метода может быть реализован следующим образом:

public class RetryMethodInterceptor implements MethodInterceptor {

  private static final Logger logger = Logger.getLogger(RetryMethodInterceptor.class.getName());

  @Override
  public Object invoke(MethodInvocation methodInvocator) throws Throwable {
    Retry retryAnnotation = methodInvocator.getMethod().getAnnotation(Retry.class);
    Set<Class<? extends Throwable>> retriableExceptions =
        Sets.newHashSet(retryAnnotation.retryOnExceptions());

    String className = methodInvocator.getThis().getClass().getCanonicalName();
    String methodName = methodInvocator.getMethod().getName();

    int tryCount = 0;
    while (true) {
      try {
        return methodInvocator.proceed();
      } catch (Throwable ex) {
        tryCount++;
        boolean isExceptionInAllowedList = isRetriableException(retriableExceptions, ex.getClass());
        if (!isExceptionInAllowedList) {
          System.out.println(String.format(
              "Exception not in retry list for class: %s - method: %s - retry count: %s",
              className, methodName, tryCount));
          throw ex;
        } else if (isExceptionInAllowedList && tryCount > retryAnnotation.maxTries()) {
          System.out.println(String
                  .format(
                      "Exhausted retries, rethrowing exception for class: %s - method: %s - retry count: %s",
                      className, methodName, tryCount));
          throw ex;
        }
        System.out.println(String.format("Retrying for class: %s - method: %s - retry count: %s",
            className, methodName, tryCount));
      }
    }
  }

  private boolean isRetriableException(Set<Class<? extends Throwable>> allowedExceptions,
      Class<? extends Throwable> caughtException) {
    for (Class<? extends Throwable> look : allowedExceptions) {
      // Only compare the class names we do not want to compare superclass so Class#isAssignableFrom
      // can't be used.
      if (caughtException.getCanonicalName().equalsIgnoreCase(look.getCanonicalName())) {
        return true;
      }
    }
    return false;
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...