본문 바로가기
Spring

[Spring] 재시도할 때 사용하는 @Retryable, @Recover 사용법

by 노력남자 2024. 2. 26.
반응형

이번 포스팅에선 Spring에서 재시도할 때 사용하는 어노테이션 @Retryable, @Recover에 대해 알아보겠다.

 

@Retryable이란?

 

Bean의 메소드 실행 도중 exception이 발생한 경우 재시도를 할 수 있게 해주는 어노테이션

 

@Retryable 사용 방법

 

1. spring-retry 의존성 추가

 

implementation("org.springframework.retry:spring-retry")

 

 

2. Spring Application에 @EnableRetry 추가

 

@EnableRetry
@SpringBootApplication
class SpringApplication

 

3. 재시도할 메서드에 @Retryable 추가

 

@Service
class RetryableService {

    @Retryable
    fun retryableMethod() {
        ...
    }
}

 

@Retryable 옵션

 

maxAttempts, maxAttemtsExpression(SpEL 형식)

 

최대 재시도 횟수다. 기본 값은 3이다.

 

@Service
class RetryableService {

    @Retryable(maxAttempts = 4)
    fun retryableMethod() {
        ...
    }
}

 

recover

 

maxAttempts까지 재시도를 했는데 성공을 못 한 경우, 호출할 메소드를 정의하는 거다.

 

recover에 사용할 메소드는 반드시 @Recover이 있어야 한다.

 

@Service
class RetryableService {

    @Retryable(recover = "recoverMethod2")
    fun retryableMethod() {
        ...
    }

    @Recover
    fun recoverMethod() {
        ...
    }

    @Recover
    fun recoverMethod2() {
        ...
    }

    @Recover
    fun recoverMethod3() {
        ...
    }
}

 

@Recover가 하나만 있는 경우엔 recover를 생략할 수 있다.

 

@Service
class RetryableService {

    @Retryable
    fun retryableMethod() {
        ...
    }

    @Recover
    fun recoverMethod() {
        ...
    }
}

 

@Recover가 2개 이상 있는데 recover를 안 쓰면 맨 아래에 있는 메소드가 사용된다.

 

@Service
class RetryableService {

    @Retryable
    fun retryableMethod() {
        ...
    }

    @Recover
    fun recoverMethod() {
        ...
    }

    @Recover
    fun recoverMethod2() {
        ...
    }

    @Recover << 맨 마지막 거 실행
    fun recoverMethod3() {
        ...
    }
}

 

존재하지 않는 recover를 사용한 경우

 

@Service
class RetryableService {

    @Retryable(recover = "recoverMethod")
    fun retryableMethod() {
        ...
    }
}

 

maxAttempts만큼 실행이 추가로 되고 "org.springframework.retry.ExhaustedRetryException: Cannot locate recovery method' 에러가 발생한다.

 

 

backoff

 

재시도 시간을 조절할 때 사용한다.

 

- delay: 지연 시간 (ms)

- maxDelay: 최대 지연 시간

- multiplier: 재시도마다 delay * multiplier만큼 지연 시간 추가

 

@Service
class RetryableService {

    @Retryable(
        recover = "recoverMethod", 
        backoff = Backoff(
            delay = 1000,
            maxDelay = 3000,
            multiplier = 2.0
        )
    )
    fun retryableMethod() {
        ...
    }
}

 

- random: 지연 시간을 랜덤으로 설정

 

@Service
class RetryableService {

    @Retryable(
        recover = "recoverMethod",
        backoff = Backoff(
            random = true,
            maxDelay = 1000
        )
    )
    fun retryableMethod() {
        ...
    }
}

 

- delayExpression, maxDelayExpression, multiplierExpression, randomExpression: 각 옵션을 SpEL로 표현 가능

 

// 이전 재시도 간격의 두 배를 사용하여 백오프 간격을 설정
multiplierExpression = "#lastInterval * 2" 

// 현재 재시도 횟수에 따라 지수적으로 증가하는 값을 사용하여 백오프 간격을 설정
multiplierExpression = "T(java.lang.Math).pow(2, #currentRetryContext.retryCount) * 1000

 

include (= value, retryFor), exceptionExpression(SpEL 형식)

 

retryable 대상 Exception 타입 설정할 때 사용

 

@Service
class RetryableService {

    @Retryable(include = [ReadTimeoutException::class])
    fun retryableMethod() {
        ...
    }
}

 

exclude (= noRetryFor)

 

retryable 대상 제외 Exception 타입 설정할 때 사용

 

@Service
class RetryableService {

    @Retryable(exclude = [ReadTimeoutException::class])
    fun retryableMethod() {
        ...
    }
}

 

notRecoverable

 

recover 대상 제외 Exception 타입 설정할 때 사용

 

@Service
class RetryableService {

    @Retryable(notRecoverable = [ReadTimeoutException::class])
    fun retryableMethod() {
        ...
    }
}

 

이외에 label, stateful, interceptor, listeners 가 있는데 정확한 사용처를 모르겠어서 나중에 알게되면 추가하겠다.

 

현재 Retry 횟수 정보를 알고 싶다면?

 

RetrySynchronizationManager.getContext()로 RetryContext를 꺼내서 사용하면 된다.

 

RetrySynchronizationManager.getContext()?.retryCount

 

반응형

댓글