Облачные платформы, такие как 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;
}
}