Руководство по Spring Retry

Путеводитель по Spring Retry

1. обзор

Spring Retry предоставляет возможность автоматического повторного вызова неудачной операции. Это полезно, когда ошибки могут быть временными (например, кратковременный сбой сети). Spring Retry обеспечивает декларативное управление процессом и поведением на основе политик, которое легко расширять и настраивать.

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

2. Maven Зависимости

Начнем с добавления зависимости в нашpom.xml:


    org.springframework.retry
    spring-retry
    1.1.5.RELEASE

Мы можем проверить последнюю версиюspring-retry вMaven Central.

Нам также нужно добавить Spring AOP в наш pom.xml:


    org.springframework
    spring-aspects

3. Включение Spring Retry

Чтобы включить Spring Retry в приложении, нам нужно добавить аннотацию@EnableRetry в наш класс@Configuration:

@Configuration
@EnableRetry
public class AppConfig { ... }

4. Повторить с аннотациями

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

4.1. @Retryableс

Чтобы добавить в методы функцию повтора, можно использовать@Retryable:

@Service
public interface MyService {
    @Retryable(
      value = { SQLException.class },
      maxAttempts = 2,
      backoff = @Backoff(delay = 5000))
    void retryService(String sql) throws SQLException;
    ...
}

Здесь поведение повтора настраивается с использованием атрибутов@Retryable.. В этом примере попытка повтора будет предпринята, только если метод выдаетSQLException.. Будет до 2 повторов и задержка в 5000 миллисекунд.

Если@Retryable используется без каких-либо атрибутов, если метод не работает из-за исключения, то повторная попытка будет предпринята до трех раз с задержкой в ​​одну секунду.

4.2. @Recoverс

Аннотация@Recover используется для определения отдельного метода восстановления, когда метод@Retryable дает сбой с указанным исключением:

@Service
public interface MyService {
    ...
    @Recover
    void recover(SQLException e, String sql);
}

Поэтому, если методretryService() выдаетSQLException, будет вызван методrecover(). Подходящий обработчик восстановления имеет свой первый параметр типаThrowable (необязательно). S ubsequent аргументы заполняются из списка аргументов метода с ошибкой в ​​том же порядке, что и метод с ошибкой, и с тем же типом возвращаемого значения.

5. RetryTemplateс

5.1 RetryOperations

Spring Retry предоставляет интерфейсRetryOperations, который предоставляет набор методовexecute():

public interface RetryOperations {
     T execute(RetryCallback retryCallback) throws Exception;

    ...
}

RetryCallback, который является параметромexecute(), представляет собой интерфейс, который позволяет вставлять бизнес-логику, которую необходимо повторить в случае сбоя:

public interface RetryCallback {
    T doWithRetry(RetryContext context) throws Throwable;
}

5.2. RetryTemplate Конфигурация

RetryTemplate - это реализацияRetryOperations. Давайте настроим bean-компонентRetryTemplate в нашем классе@Configuration:

@Configuration
public class AppConfig {
    //...
    @Bean
    public RetryTemplate retryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();

        FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
        fixedBackOffPolicy.setBackOffPeriod(2000l);
        retryTemplate.setBackOffPolicy(fixedBackOffPolicy);

        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(2);
        retryTemplate.setRetryPolicy(retryPolicy);

        return retryTemplate;
    }
}

RetryPolicy определяет, когда следует повторить операцию. SimpleRetryPolicy используется для повтора фиксированного числа раз.

BackOffPolicy используется для управления откатом между попытками повторения. FixedBackOffPolicy делает паузу на фиксированный период времени перед продолжением.

5.3. ИспользуяRetryTemplate

Чтобы запустить код с обработкой повторов, мы вызываем retryTemplate.execute(): __

retryTemplate.execute(new RetryCallback() {
    @Override
    public Void doWithRetry(RetryContext arg0) {
        myService.templateRetryService();
        ...
    }
});

То же самое может быть достигнуто с помощью лямбда-выражения вместо анонимного класса: __

retryTemplate.execute(arg0 -> {
    myService.templateRetryService();
    return null;
});

6. Конфигурация XML

Spring Retry может быть настроен с помощью XML с использованием пространства имен Spring AOP.

6.1. Добавление файла XML

В пути к классам добавимretryadvice.xml:

...

    
        
        
    

    
        
    

    
        
        
    

    
        
        
            
                
            
        
    

    
        
        
        
        
        
        
    

...

В этом примере используется пользовательскийRetryTemplate внутри перехватчика методаxmlRetryService.

6.2. Использование конфигурации XML

Импортируйтеretryadvice.xml изclasspath и включите поддержку@AspectJ:

@Configuration
@EnableRetry
@EnableAspectJAutoProxy
@ImportResource("classpath:/retryadvice.xml")
public class AppConfig { ... }

7. Слушатели

Слушатели предоставляют дополнительные обратные вызовы при повторных попытках. Их можно использовать для решения различных сквозных задач при разных попытках.

7.1. Добавление обратных вызовов

Обратные вызовы предоставляются в интерфейсеRetryListener:

public class DefaultListenerSupport extends RetryListenerSupport {
    @Override
    public  void close(RetryContext context,
      RetryCallback callback, Throwable throwable) {
        logger.info("onClose);
        ...
        super.close(context, callback, throwable);
    }

    @Override
    public  void onError(RetryContext context,
      RetryCallback callback, Throwable throwable) {
        logger.info("onError");
        ...
        super.onError(context, callback, throwable);
    }

    @Override
    public  boolean open(RetryContext context,
      RetryCallback callback) {
        logger.info("onOpen);
        ...
        return super.open(context, callback);
    }
}

Обратные вызовыopen иclose поступают до и после всего повтора, аonError применяется к отдельным вызовамRetryCallback.

7.2. Регистрация слушателя

Затем мы регистрируем наш слушатель (DefaultListenerSupport) в нашем bean-компонентеRetryTemplate:

@Configuration
public class AppConfig {
    ...

    @Bean
    public RetryTemplate retryTemplate() {
        RetryTemplate retryTemplate = new RetryTemplate();
        ...
        retryTemplate.registerListener(new DefaultListenerSupport());
        return retryTemplate;
    }
}

8. Тестирование результатов

Проверим результаты:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(
  classes = AppConfig.class,
  loader = AnnotationConfigContextLoader.class)
public class SpringRetryTest {

    @Autowired
    private MyService myService;

    @Autowired
    private RetryTemplate retryTemplate;

    @Test(expected = RuntimeException.class)
    public void givenTemplateRetryService_whenCallWithException_thenRetry() {
        retryTemplate.execute(arg0 -> {
            myService.templateRetryService();
            return null;
        });
    }
}

Когда мы запускаем тестовый пример, приведенный ниже текст журнала означает, что мы успешно настроилиRetryTemplate и Listener:

2017-01-09 20:04:10 [main] INFO  o.b.s.DefaultListenerSupport - onOpen
2017-01-09 20:04:10 [main] INFO  o.example.springretry.MyServiceImpl
- throw RuntimeException in method templateRetryService()
2017-01-09 20:04:10 [main] INFO  o.b.s.DefaultListenerSupport - onError
2017-01-09 20:04:12 [main] INFO  o.example.springretry.MyServiceImpl
- throw RuntimeException in method templateRetryService()
2017-01-09 20:04:12 [main] INFO  o.b.s.DefaultListenerSupport - onError
2017-01-09 20:04:12 [main] INFO  o.b.s.DefaultListenerSupport - onClose

9. Заключение

В этой статье мы представили Spring Retry. Мы видели примеры повторных попыток с использованием аннотаций иRetryTemplate. Затем мы настроили дополнительные обратные вызовы с использованием слушателей.

Вы можете найти исходный код этой статьиover on GitHub.