Exception
μ΄ λ°μνμ λ μλμΌλ‘ ν΄λΉ λ©μλλ₯Ό λ€μ νΈμΆν΄μ£Όλ νλ‘μλ₯Ό Spring AOPλ₯Ό μ΄μ©ν΄μ ꡬννλ€.
νΉμ μμ²μ΄ μ¬μλμ μν΄ μμΈλ₯Ό νΌν μ μλ κ°λ₯μ±μ΄ μλ€λ©΄ μλ² μΈ‘μμ μ ν΄μ§ νμλ‘ μ¬μλνλ κ²μ ν΄λΌμ΄μΈνΈ μ μ₯μμ λμμ§ μμ λ°©μμ΄λ€.
μλ³Έ μ½λλ₯Ό 건λ€μ§ μκ³ μ μ©νκ³ μ νλ λ©μλμ @Retry
λ₯Ό λΆμ¬μ£Όλ κ²λ§μΌλ‘ μ μ©λλλ‘ ν κ²μ΄λ€.
μλ³Έ μ½λλ₯Ό 건λ€μ§ μκ³ λΉμ¦λμ€ λ‘μ§μ΄ μλ μ¬μλ λ‘μ§μ μΆκ°νλ κ²μ΄λ―λ‘ νλ‘μ ν¨ν΄
μ΄ μ λΉνκ³ νλ‘μ ν¨ν΄
μ Springμμ λ³΄λ€ μ½κ² μ¬μ©νκΈ° μν΄ Spring AOP
λ₯Ό μ¬μ©νλ€.
[ Spring AOP ]
implementation 'org.springframework.boot:spring-boot-starter-aop'
repository
μ save
λ©μλλ 5λ²μ§Έ μ κ·Όλ§λ€ μμΈκ° λ°μνλ€.
@Repository
public class ExamRepository {
private static int sequence = 0;
// 5λ²μ§Έ μμ²λ§λ€ μμΈλ₯Ό λ°μμν¨λ€.
public String save(String itemId) {
++sequence;
if (sequence%5 == 0) {
throw new IllegalStateException("μμΈ λ°μ");
}
return "ok";
}
}
μλλ 5λ²μ§Έ μμ²λ§λ€ μμΈκ° 리ν΄λμ§λ§ μμΈλ°μ μ μ¬μλλ₯Ό ν΅ν΄ μμΈλ₯Ό ννΌνλλ‘ ν κ²μ΄λ€.
@Retry
κ° λΆμ λ©μλμ μ¬μλ Adviceκ° μ μ©λλλ‘ ν κ²μ΄λ€.
μ±κ³΅ν λκΉμ§ 무νμ μΌλ‘ μ¬μλκ° λμ§ μλλ‘ νκ³ , μ μ©λ λ©μλλ§λ€ μ΅λ μ¬μλ νμλ₯Ό λ¬λ¦¬νκΈ° μν΄ μμ±μΌλ‘ value
λ₯Ό ν¬ν¨νλ€.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Retry {
int value() default 3;
}
JoinPoint
μ νλ‘ μμΈμ λν μ²λ¦¬ λ± μΆκ°μ μΈ μμ
μ΄ νμνκΈ° λλ¬Έμ @Around
λ₯Ό μ¬μ©νλ€.
@annotation(μ΄λ
Έν
μ΄μ
_μ΄λ¦)
μ΄λ
Έν
μ΄μ
μ μ΄λ¦μ μ§μ νκ³ ν΄λΉ νμ
μ μΈμλ‘ λ°μΌλ―λ‘ PointCutμ μ§μ νκ³ μλ€.
λ§μ½ μ΄λ Έν μ΄μ μ μΈμλ‘ λ°μ§ μλλ€λ©΄ @annotation() μλ μ΄λ Έν μ΄μ μ ν¨ν€μ§ κ²½λ‘μ νμ μ΄ λ€μ΄κ°μΌ νλ€.
Retry
μ΄λ
Έν
μ΄μ
μμ μ΅λ μ¬μλ νμλ₯Ό κ°μ Έμ€κ³ μ¬μλ νμλ§νΌ JoinPoint
λ₯Ό νΈμΆνλ€. (proceed()
);
μμ² μ€ μμΈκ° λ°μνλ€λ©΄ λ°λ‘ μμΈλ₯Ό λμ§μ§ μκ³ μΌμμ μΌλ‘ μ μ₯ν λ€μ μ΅λ μ¬μλ νμλ₯Ό μ΄κ³Όνλ κ²½μ° μμ μ μ₯ν μμΈλ₯Ό λ°μμν¨λ€.
@Aspect
@Slf4j
public class RetryAspect {
@Around("@annotation(retry)")
public Object doRetry(ProceedingJoinPoint joinPoint, Retry retry) throws Throwable {
log.info("[Retry] {} retry={}", joinPoint.getSignature(), retry);
int maxRetry = retry.value();
Exception exceptionHolder = null;
for (int retryCnt=1; retryCnt<=maxRetry; retryCnt++) { // μ¬μλ
if (retryCnt > 1) {
log.info("{} λ²μ§Έ μ¬μλ", retryCnt-1);
}
try {
return joinPoint.proceed(); // target νΈμΆμ μμΈκ° μλ€λ©΄ κ·Έλλ‘ λ°ν
} catch (Exception e) {
exceptionHolder = e; // μμΈκ° λ°μνλ€λ©΄ λ°μν μμΈλ₯Ό 보κ΄
}
}
throw exceptionHolder; // μ΅λ μ¬μλ νμλ₯Ό λμ΄μ κ²½μ° μμΈλ°μ
}
}
μ¬μλλ₯Ό μν μ΄λ Έν μ΄μ μ λΆμ΄κΈ° μ ν μ€νΈμ΄λ€.
@Slf4j
@SpringBootTest
public class RetryTest {
@Autowired
ExamRepository examRepository;
@Test
void test() {
for (int i=1;i<=5;i++) {
String result = examRepository.save(String.valueOf(i));
log.info("result={}, itemId={}", result, i);
}
}
}
1~5κΉμ§ μ΄ 5λ²μ μμ²μ νλλ° 4λ²μ§Έ μμ² μ΄ν μμΈκ° ν°μ§λ κ²μ νμΈνλ€.
μ¬μλ λ‘μ§μ μ μ©νκ³ μ νλ λ©μλμ @Retry
λ₯Ό λΆμ¬μ£Όλ―λ‘ AOPλ₯Ό μ μ©νλ€.
@Repository
public class ExamRepository {
private static int sequence = 0;
// 5λ²μ§Έ μμ²λ§λ€ μμΈλ₯Ό λ°μμν¨λ€.
@Retry
public String save(String itemId) {
++sequence;
if (sequence%5 == 0) {
throw new IllegalStateException("μμΈ λ°μ");
}
return "ok";
}
}
μ€νλ§ μ»¨ν
μ΄λμ μ¬μλ Aspectλ₯Ό μ¬λ €μ€μΌ νλ―λ‘ @Import
λ₯Ό μ¬μ©ν΄μ λ±λ‘νλ€.
@Import(RetryAspect.class)
@Slf4j
@SpringBootTest
public class RetryTest {
...
}
μ΄μ 5λ²μ§Έ μμ²λ§λ€ μ¬μλκ° λ°μνκ³ μ¬μλμ μν΄ μμ²μ΄ μ μμ μΌλ‘ μλ΅λ κ²μ΄λ€.
4λ²μ§Έ μμ²μμ ν λ²μΌλ‘ μ¬μλκ° λ°μνκ³ 5λ²μ§Έ μμ²μ΄ μ±κ³΅ν κ²μ νμΈν μ μλ€.
μλ κ°μλ₯Ό 100% μ°Έκ³ νμ¬ μ 리ν λ΄μ©μ
λλ€.
κ°μμλ£λ₯Ό κ·Έλλ‘ κ°μ Έμ¨ κ²μ μλλ λ³΄λ€ μ νν μ 보λ₯Ό μνλ€λ©΄ κ°μλ₯Ό λ€μ΄μ£ΌμΈμ. (κ°μΆ!)
μΈνλ° - μ€νλ§ ν΅μ¬ μ리 κ³ κΈνΈ (κΉμν λ)