ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 재시도 AOP
    스프링/스프링 AOP 2023. 9. 23. 21:48

    단순 조회를 할 때 아주 가끔씩 간헐적으로 예외가 발생하는 경우가 있다. 단순 조회이기 때문에 그냥 새로고침을 눌러주면 된다. 이럴 때에는 재시도 AOP를 만들어서 예외가 가끔 발생하더라도 다시 재시도를 해주면 된다.

     

     

    Retry 어노테이션

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Retry {
        int value() default 3;
    }

    Retry 어노테이션을 선언해줄 때 int value() default 3;이라는 변수를 선언해준다. 이 변수는 RetryAspect에서 재시도를 하는 default 값으로 사용될 것이다.

     

     

     

    RetryAspect.class

    @Slf4j
    @Aspect
    public class RetryAspect {
    
        @Around("@annotation(retry)")
        public Object doRetry(ProceedingJoinPoint joinPoint, Retry retry) throws Throwable {
        // 파라미터에 있는 Retry가 타입이라 @Around 안에가 대체 됨
    
            log.info("[retry] {} retry={}", joinPoint.getSignature(), retry);
    
            int maxRetry = retry.value(); // 횟수 제한은 무조건 있어야함
            Exception exceptionHolder = null; // 예외 담아두기
    
            for (int retryCount = 1; retryCount <= maxRetry; retryCount++) {
                try {
                    log.info("[retry] try count={}/{}", retryCount, maxRetry);
                    return joinPoint.proceed();
                } catch (Exception e) {
                    exceptionHolder = e;
                }
            }
            throw exceptionHolder;
        }
    }

    재시도를 해주는 aop이다. @Around의 경로는 doRetry의 파라미터 안에 있는 Retry 타입으로 대체할 수 있다.

     

    로직을 보면 retry.value()에서 default 값인 3을 가져온 다음 maxRetry만큼 루프를 돈다.

    이 때, 성공하면 joinPoint.proceed()를 return 한다.

     

    exceptionHolder를 선언해주고 사용하고 있는데, 에러가 났을 때 catch를 해서 최후에 throw를 던져줄 수 있는 용도이다.

    만약에 2번째까지 예외가 났을 때, 3번째에서 로직이 잘 수행되면 return joinPoint.proceed()로 인해 예외가 던져지지 않는다. 하지만 3번째까지 예외가 나면 joinPoint.proceed()를 반환하지 않아서 예외가 throw 된다.

     

     

    @Repository
    public class ExamRepository {
    
        private static int seq = 0;
    
        /**
         * 5번에 1번 실패하는 요청
         */
        @Trace
        @Retry(4) // default(재시도) 값 여기서 바꿔줄 수 있음
        public String save(String itemId) {
            seq++;
            if (seq % 5 == 0) {
                throw new IllegalStateException("예외 발생");
            }
            return "ok";
        }
    }

    @Trace 밑에 @Retry 어노테이션을 붙여줘서 aop를 하나 더 걸어줬다. 어노테이션 안에 숫자를 넣어주면 default 값이 되어서 이 숫자만큼 재시도를 해주는 것이다.

     

     

    @Slf4j
    @Import({TraceAspect.class, RetryAspect.class})
    @SpringBootTest
    public class ExamTest {
    
        @Autowired
        ExamService examService;
    
        @Test
        void test() {
            for (int i = 0; i < 5; i++) {
                log.info("client request i={}", i);
                examService.request("data" + i);
            }
        }
    }

    테스트코드를 실행해보면 로직이 제대로 설계되었는지 확인할 수 있다.

     

    5번째인 data4에서 에러가 발생했지만 Retry aop로 인해서 재시도를 하게 되어서 예외가 발생하지 않고 정상 응답이 되었다.

     

     

    재시도 aop를 사용할 때에는 무조건 default 값을 정해두어야 한다는 것을 명심해야한다.

    '스프링 > 스프링 AOP' 카테고리의 다른 글

    HTTP 요청 응답 기록 - httpexchanges  (0) 2023.10.01
    AOP 주의사항 - 프록시와 내부 호출  (0) 2023.09.23
    어노테이션으로 AOP 사용  (0) 2023.09.23
    포인트컷 - execution  (0) 2023.09.23
    어드바이스 종류  (0) 2023.09.22
Designed by Tistory.