가. Spring Retry 설정법
1) 의존성 적용
•
gradle 기반 의존성 적용
// usecase-core 모듈 내 gradle에 적용
dependencies {
implementation("org.springframework.retry:spring-retry")
}
Kotlin
복사
2) EnableRetry 설정
•
기본 설정
@EnableRetry
@SpringBootApplication
class PointManagementApplication
fun main(args: Array<String>) {
runApplication<PointManagementApplication>(*args)
}
Kotlin
복사
•
retry 적용을 위해 Configuration에 @EnableRetry 적용
◦
Retry의 우선순위 조정 설정, 참고로 기본 값은 LOWEST_PRECEDENCE
@Configuration
@EnableRetry
class AppConfig {
}
@Configuration
class RetryConfig : RetryConfiguration() {
override fun getOrder() = Ordered.HIGHEST_PRECEDENCE
}
Kotlin
복사
나. 활용 예시
1) 기본 사용법
•
@Retryable로 정의된 메소드는 value로 지정된 예외 발생 시 기본 설정에 따라 1초 간격으로 3회 재시도
•
@Recover로 메소드가 정의되었다면 @Retryable로 정의된 메소드가 3번 재시도 후 실행
@Service
public interface MyService {
@Retryable(value = SQLException.class)
void retryServiceWithRecovery(String sql) throws SQLException;
@Recover
void recover(SQLException e, String sql);
}
Kotlin
복사
code from https://www.baeldung.com/spring-retry
2) 동시성 제어
•
@Retryable을 커스텀하면 낙관적 락이 걸린 상태에서 동시성 및 정합성 이슈에 효과적으로 대응 가능
•
backoff 설정은 재시도 전 기다리는 시간에 대한 설정임. 아래와 같이 backoff가 설정되었다면 다음과 같이 시간 간격을 두고 재시도
•
첫번째 예외 발생 후 100ms 뒤에 재시도, 두 번째 예외 발생 후 200ms 뒤에 재시도, 세 번재 예외 발생 후 400ms 뒤에 재시도
@Retryable(
value = [ObjectOptimisticLockingFailureException::class, DataIntegrityViolationException::class],
backoff = Backoff(delay = 100, multiplier = 2.0)
)
@Transactional
fun attachFiles(cmd: UpdateImageFilesCommand) {
val image = repository.get(cmd.imageId)
?: throw NotExistDigitalAssetException(DigitalAssetType.STOCK_IMAGE, cmd.imageId.toString())
image.attach(cmd.files)
repository.saveAndFlush(image)
}
Kotlin
복사
3) 메시지 재처리
•
처리되지 못한 메시지를 주기적으로 수집해서 재처리
•
아래의 설정에 따라 retryWorker가 1분(60000ms)마다 500개의 메시지를 수집해서 재처리함
@Profile("prod|stg")
@Component
class RetryMsgScheduler(val retryWorker: RetryWorker) {
private val logger = LoggerFactory.getLogger(javaClass)
companion object {
const val FETCH_SIZE = 500
}
@Scheduled(fixedDelay = 60000)
fun schedule() {
val now = LocalDateTime.now()
logger.info("retry scheduler started $now")
val retries = retryWorker.fetch(FETCH_SIZE)
if (retries.isEmpty()) {
return
}
retries.forEach { retryWorker.retry(it.id!!) }
}
}
Kotlin
복사