1. 개요

Spring Retry는 실패한 작업을 자동으로 다시 호출하는 기능을 제공합니다. 이는 오류가 일시적일 수 있는 경우에 유용합니다(예: 일시적인 네트워크 결함).

이 예제에서는 Spring Retry 를 사용하는 다양한 방법( 어노테이션, RetryTemplate  및 콜백)을 볼 것입니다.

2. 메이븐 의존성

pom.xml 파일에 spring-retry 의존성을 추가하는 것으로 시작하겠습니다 .

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.2.5.RELEASE</version>
</dependency>

또한 프로젝트에 Spring AOP를 추가해야 합니다.

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.2.8.RELEASE</version>
</dependency>

최신 버전의 spring-retryspring-aspect 의존성에 대해서는 Maven Central을 살펴보십시오 .

3. 스프링 재시도 활성화

애플리케이션에서 Spring Retry를 활성화하려면 @Configuration 클래스에 @EnableRetry 어노테이션추가해야 합니다 .

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

4. 스프링 재시도 사용

4.1. @ 복구 없이 재시 도 가능

@Retryable 어노테이션을 사용하여 메소드에 재시도 기능을 추가 할 수 있습니다 .

@Service
public interface MyService {
    @Retryable(value = RuntimeException.class)
    void retryService(String sql);

}

여기에서 RuntimeException 이 발생 하면 재시도가 시도됩니다 .

@Retryable 의 기본 동작, 재시도 재시도 사이의 1 초 지연, 세 번까지 발생할 수 있습니다.

4.2. @Retryable@Recover

이제 @Recover 어노테이션을 사용하여 복구 방법을 추가해 보겠습니다 .

@Service
public interface MyService {
    @Retryable(value = SQLException.class)
    void retryServiceWithRecovery(String sql) throws SQLException;
        
    @Recover
    void recover(SQLException e, String sql);
}

여기에서 SQLException 이 throw 되면 재시도가 시도됩니다 . @Recover의 때 애노테이션은 별도의 복구 방법을 정의 @Retryable 방법이 특정 예외 실패.

결과적으로 retryServiceWithRecovery 메서드가 세 번 시도한 후에도 SqlException계속 throw 하면 recover() 메서드가 호출됩니다.

복구 핸들러에는 Throwable (선택 사항) 유형의 첫 번째 매개변수 와 동일한 반환 유형이 있어야 합니다.  다음 인수는 실패한 메소드의 인수 List에서 동일한 순서로 채워집니다.

4.3. @Retryable의 동작 사용자 정의

재시도 동작을 사용자 정의하기 위해 maxAttemptsbackoff 매개변수를 사용할 수 있습니다 .

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

최대 2회의 시도와 100밀리초의 지연이 있습니다.

4.4. 스프링 속성 사용

@Retryable 어노테이션 에서 속성을 사용할 수도 있습니다 .

이를 시연하기 위해 delaymaxAttempts 값을  속성 파일로 외부화하는 방법을 살펴보겠습니다 .

먼저 retryConfig 라는 파일에 속성을 정의해 보겠습니다. 속성 :

retry.maxAttempts=2
retry.maxDelay=100

그런 다음 @Configuration 클래스에 이 파일을 로드 하도록 지시 합니다.

// ...
@PropertySource("classpath:retryConfig.properties")
public class AppConfig { ... }

마지막으로, 우리는의 값을 삽입 할 수 retry.maxAttemptsretry.maxDelay  우리의 @Retryable 정의를 :

@Service 
public interface MyService { 
  @Retryable( value = SQLException.class, maxAttemptsExpression = "${retry.maxAttempts}",
            backoff = @Backoff(delayExpression = "${retry.maxDelay}")) 
  void retryServiceWithExternalizedConfiguration(String sql) throws SQLException; 
}

주의하시기 바랍니다 우리가 지금 사용하고있는 maxAttemptsExpressiondelayExpression 대신 maxAttempts지연 .

5. 템플릿 재시도

5.1. 재시도 작업

Spring Retry는 일련의 execute() 메서드를 제공하는 RetryOperations 인터페이스를 제공 합니다.

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

    ...
}

RetryCallback 의 파라미터이며, () 실행은 , 필요에 따라 실패 시도하는 것을 비즈니스 로직의 삽입을 허용하는 인터페이스이다 :

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

5.2. 재시도 템플릿 구성

RetryTemplate는 의 구현입니다 RetryOperations .

@Configuration 클래스 에서 RetryTemplate 빈을 구성해 보겠습니다 .

@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 사용

재시도 처리로 코드를 실행하려면 r etryTemplate.execute() 메서드를 호출할 수 있습니다 .

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

익명 클래스 대신 람다 식을 사용할 수 있습니다.

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

6. 청취자

리스너는 재시도 시 추가 콜백을 제공합니다. 그리고 이를 다양한 재시도에 걸쳐 다양한 교차 문제에 사용할 수 있습니다.

6.1. 콜백 추가

콜백은 RetryListener 인터페이스 에서 제공됩니다 .

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);
    }
}

개방가까운 동안 콜백은, 전에 전체 재시도 후 온 의 OnError가 개인에게 적용 RetryCallback의 호출.

6.2. 리스너 등록

다음으로 RetryTemplate 빈에 리스너( DefaultListenerSupport)등록합니다 .

@Configuration
public class AppConfig {
    ...

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

7. 결과 테스트

예제를 마치기 위해 결과를 확인하겠습니다.

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

    @Autowired
    private MyService myService;

    @Autowired
    private RetryTemplate retryTemplate;

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

테스트 로그에서 볼 수 있듯이 RetryTemplateRetryListener를 적절하게 구성했습니다 .

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

8. 결론

이 기사에서는 어노테이션, RetryTemplate  및 콜백 리스너 를 사용하여 Spring Retry를 사용하는 방법을 보았습니다 .

예제의 소스 코드는 GitHub에서 사용할 수 있습니다 .

Generic footer banner