Так ли должна работать spring @Retryable?

#spring-boot #retrofit2 #spring-retry

Вопрос:

Недавно я добавил spring @Retryable для обработки 502 ошибок вместо соответствующих ответов с целевого сервера. (с дооснащением2).

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

 class BadGatewayException : RuntimeException

@Retryable(include = [BadGatewayException::class])
class A {

    private fun handle(block: () -> Call<T>): T {
        try {
            val response = val response = block().execute()
           
            ...      
      
            if (response.code() == 502) {
                throw BadGatewayException("The server suffers temporary connection problems.")
            }
            ...
        } catch(e: Exception) {
            throw RuntimeException("a system error has occurred")
        }
    }

}
 

Я ожидал @Retryable , что не буду повторять попытку, так как BadGatewayException то, что происходит с 502, будет RuntimeException сразу же завернуто в блок захвата, а затем брошено. Но, когда он был протестирован, казалось, что он следует этому шагу

  1. попробуйте получить ответ на запрос на модернизацию
  2. 502 происходит
  3. BadGatewayException брошенный
  4. повторите попытку (3 по умолчанию) — здесь каким-то образом поймано исключение BadGatewayException
  5. RuntimeException брошенный

Дело в том, @Retryable предполагается ли перехватывать какие-либо исключения таким образом? Или я что-то здесь упускаю?

Ответ №1:

SimpleRetryPolicy Используемое аннотацией не пересекает cause цепочку для поиска классифицированных исключений; только исключение верхнего уровня сравнивается со списком классифицированных.

Вам придется подключить свой собственный RetryInterceptor с соответствующим образом настроенным RetryTemplate и. SimpleRetryPolicy

     /**
     * Create a builder for a stateless retry interceptor.
     * @return The interceptor builder.
     */
    public static StatelessRetryInterceptorBuilder stateless() {
        return new StatelessRetryInterceptorBuilder();
    }
 
 RetryInterceptorBuilder.stateless()
    .retryPolicy(new SimpleRetryPolicy(...))
    .build();
 

Видеть

     /**
     * Create a {@link SimpleRetryPolicy} with the specified number of retry attempts. If
     * traverseCauses is true, the exception causes will be traversed until a match or the
     * root cause is found. The default value indicates whether to retry or not for
     * exceptions (or super classes thereof) that are not found in the map.
     * @param maxAttempts the maximum number of attempts
     * @param retryableExceptions the map of exceptions that are retryable based on the
     * map value (true/false).
     * @param traverseCauses true to traverse the exception cause chain until a classified
     * exception is found or the root cause is reached.
     * @param defaultValue the default action.
     */
    public SimpleRetryPolicy(int maxAttempts, Map<Class<? extends Throwable>, Boolean> retryableExceptions,
            boolean traverseCauses, boolean defaultValue) {
 

( defaultValue = true и Map.of(BadGatewayException.class, false) ).

Задайте имя компонента перехватчика в свойстве interceptor аннотации.