Java “实施的最佳方式”;“重试”;当到达api端点时?

Java “实施的最佳方式”;“重试”;当到达api端点时?,java,api,http,endpoint,Java,Api,Http,Endpoint,我正在使用ApacheHttpClient 我有一个Java方法(在Java微服务中),它向外部端点(我不拥有的端点)发出HttpPOST请求。通常情况下,一切正常,但有时端点出现故障并失败。代码如下所示(简化): 我没有编写原始代码,但正如您所看到的,它似乎发出了一个请求,虽然请求重试计数大于0(看起来总是这样),但它将尝试向url发出帖子。由于某种原因,它看起来有一个断点,因此在它跳入try块之后,它总是会中断,并且没有重试机制 Java中是否有一种通用的设计模式来实现命中外部端点的重试模式

我正在使用ApacheHttpClient

我有一个Java方法(在Java微服务中),它向外部端点(我不拥有的端点)发出HttpPOST请求。通常情况下,一切正常,但有时端点出现故障并失败。代码如下所示(简化):

我没有编写原始代码,但正如您所看到的,它似乎发出了一个请求,虽然请求重试计数大于0(看起来总是这样),但它将尝试向url发出帖子。由于某种原因,它看起来有一个断点,因此在它跳入
try
块之后,它总是会中断,并且没有重试机制


Java中是否有一种通用的设计模式来实现命中外部端点的重试模式?我知道在Javascript中,您可以使用Fetch API并返回一个承诺,Java有类似的功能吗?

像GCP和AWS这样的云平台通常都有自己的重试策略,这应该是首选方法

如果您想扮演自己的重试策略角色,指数退避可以是一个很好的起点

它可以是基于注释的,您可以在其中注释客户机方法。例如, 您可以按如下方式注释API方法:

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;
  }
}
@重试(maxTries=3,retryOnExceptions={RpcException.class}) public UserInfo getUserInfo(字符串userId)

@已记录
@保留(RetentionPolicy.RUNTIME)
@目标(ElementType.METHOD)
公共@接口重试{
int maxTries()默认值为0;
/**
*如果引发以下异常之一,请尝试重试。
*@返回适用异常的数组
*/

ClassAPI的终结点是什么,是AWS、GCP等吗?重试已经是
ApacheHttpClient
的一部分。promise是JS代表asyn操作,promise本身不会在失败时重试。如果你想看的话,我用我已有的代码更新了我的问题。
@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;
  }
}