Spring

Retry

Bong Gu 2022. 1. 8. 06:21
728x90

Spring-retry

  • 실패한 동작을 자동으로 다시 호출하는 기능을 제공한다.
    • 일시적인 네트워크 결함과 같이 오류가 일시적 일 수 있는 경우에 유용하다.

의존성

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.2.5.RELEASE</version>
</dependency>
  • 또한 Spring AOP 의존성도 필요하지만 spring-boot-start-data-jpa에 들어있다.
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.2.8.RELEASE</version>
    </dependency>

Enabling Spring Retry

@EnableRetry
@SpringBootApplication
public class RetryApplication {
    public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

Annotation

  • @Retryable

    @Service
    public class MyService {
    
        static int retryCount = 0;
    
        @Retryable(
                value = {SQLException.class},
                maxAttempts = 2,
                backoff = @Backoff(delay = 2000))
        int countContents() throws SQLException {
            retryCount++;
            System.out.println("retryCount " + retryCount);
            if (retryCount == 2) {
                return 100;
            } else {
                throw new SQLException();
            }
        }
    }
    • 재시도할 메소드 호출을 작성할 수 있다.
    • value : SQLException 발생시키는 경우 재시도
    • maxAttempts : 최대 2번의 재시도
    • backoff : 2000 millisecond의 텀을 두고 시도
    • 아무 속성 없이 사용할 경우, 예외가 발생하면 기본적으로 1초의 지연으로 최대 3번 재시도한다.
  • @Recover

    @Retryable(
            value = {SQLIntegrityConstraintViolationException.class},
            maxAttempts = 4,
            backoff = @Backoff(delay = 2000))
    int deleteContents(String sql) throws SQLIntegrityConstraintViolationException {
        retryCount3++;
        System.out.println("retryCount " + retryCount3);
        throw new SQLIntegrityConstraintViolationException();
    }
    
    @Recover
    public int recover(SQLIntegrityConstraintViolationException e, String sql) {
        System.out.println("Recover called : message=" + e.getMessage() + ", sql=" + sql);
        return 50;
    }
    • @Retryable 메소드가 지정된 예외로 실패할 때, 별도의 복구 메소드를 정의하는데 사용된다.
    • maxAttempt만큼 재시도 후 복구가 안되었을 경우 recover() 메소드가 최종 호출됩니다.

RetryTemplate

  • execute() 메소드를 제공하는 RetryOperations 인터페이스를 제공

    public interface RetryOperation {
        <T> T execute(RetryCallback<T> retryCallback) throws Exception;
        ...
    }
    • RetryCallback
      public interface RetryCallback<T> {
          T doWithRetry(RetryContext context) throws Throwable;
      }
  • RetryTemplate는 RetryOperations의 구현체

적용

  • bean 등록

    @EnableRetry
    @SpringBootApplication
    public class RetryApplication {
        public static void main(String[] args) {
            SpringApplication.run(RetryApplication.class, args);
        }
    
        @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);
            retryTemplate.registerListener(new DefaultListenerSupport());
            return retryTemplate;
        }
    }
  • Listener

    public class DefaultListenerSupport extends RetryListenerSupport {
        @Override
        public <T, E extends Throwable> void close(RetryContext context,
          RetryCallback<T, E> callback, Throwable throwable) {
            logger.info("onClose);
            // 로직
            super.close(context, callback, throwable);
        }
    
        @Override
        public <T, E extends Throwable> void onError(RetryContext context,
          RetryCallback<T, E> callback, Throwable throwable) {
            logger.info("onError"); 
            // 로직
            super.onError(context, callback, throwable);
        }
    
        @Override
        public <T, E extends Throwable> boolean open(RetryContext context,
          RetryCallback<T, E> callback) {
            logger.info("onOpen);
            // 로직
            return super.open(context, callback);
        }
    }
    • open, close : 전체 재시도 전후
    • onError : 개발 재시도 시
  • 사용

    // callback 방식
    retryTemplate.execute(new RetryCallback<Integer, RuntimeException>()  {
                @Override
                public Integer doWithRetry(RetryContext context) {
                    return myService.countContentsForRetryTemplate();
                }
    });
    // lambda 방식
    retryTemplate.execute(context -> myService.countContentsForRetryTemplate());

참고

728x90