ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 비동기 Timelimiter 를 동기로 호출 가능하도록 변경
    Spring/CircuitBreaker 2023. 10. 10. 10:50

    목차

       

      1. 배경 지식

      dependencies

      dependencies {
          implementation 'org.springframework.boot:spring-boot-starter-aop'
          implementation 'io.github.resilience4j:resilience4j-spring6:2.1.0'
      }

       

       

      2. TimeLimiterAspectExt 상속

      https://github.com/resilience4j/resilience4j/blob/master/resilience4j-spring6/src/main/java/io/github/resilience4j/spring6/timelimiter/configure/TimeLimiterAspectExt.java

      package io.github.resilience4j.spring6.timelimiter.configure;
      
      public interface TimeLimiterAspectExt {
      
          boolean canHandleReturnType(Class<?> returnType);
      
          Object handle(ProceedingJoinPoint proceedingJoinPoint, TimeLimiter timeLimiter, String methodName) throws Throwable;
      
      }

       

      3. canHandleReturnType 구현

      @Override
      public boolean canHandleReturnType(Class<?> returnType) {
      return !(CompletionStage.class.isAssignableFrom(returnType))
          && timeLimiterAspectExtList.stream().noneMatch(ext -> ext.canHandleReturnType(returnType));
      }

      :  비동기이면 returnType 바꾸지 않기위해 false, 동기이면 returntype 바꾸기 위해 true 리턴 합니다

       

      1) CompletionStage.class.isAssignableFrom(returnType) : 주어진 클래스가 CompletionStage 클래스의 자손인지 여부를 확인합니다

      2) timeLimiterAspectExtList.stream().noneMatch(ext -> ext.canHandleReturnType(returnType) : canHandleReturnType 에서 지원되는 타입인지를 검사합니다

      • 지원되는 타입 : void,String,Number,Boolean,Iterable,Map,Object

       

      4. handle 구현

      배경지식

      ProceedingJoinPoint class

      org.aspectj.lang.ProceedingJoinPoint는 AspectJ에서 제공하는 인터페이스입니다. 이 인터페이스는 현재 실행 중인 메서드 또는 필드에 대한 정보를 제공하며, 메서드의 실행을 제어할 수 있는 기능을 제공합니다.

      ProceedingJoinPoint 인터페이스에는 다음과 같은 메서드가 정의되어 있습니다.

      • proceed(): 메서드 실행을 계속 진행합니다.
      • getThis(): 메서드가 실행 중인 객체를 반환합니다.
      • getTarget(): 메서드가 실행 중인 객체의 클래스 객체를 반환합니다.
      • getArgs(): 메서드에 전달된 인수를 반환합니다.
      • getSignature(): 메서드의 시그니처를 반환합니다.
      • getKind(): JoinPoint의 종류(메서드 실행 전/후, 예외 발생 시 등)를 반환합니다.

      Advice에서는 ProceedingJoinPoint를 이용하여 메서드 실행을 제어하거나, 메서드 실행 중에 발생한 예외를 처리할 수 있습니다. 예를 들어, 로깅 기능을 구현하는 Advice에서는 ProceedingJoinPoint를 이용하여 메서드 실행 전/후에 로그를 기록할 수 있습니다.

       

      ExposeInvocationInterceptor.currentInvocation()

      ExposeInvocationInterceptor.currentInvocation() 메서드는 현재 실행 중인 AOP Alliance MethodInvocation 객체를 반환합니다.

      이 객체는 AOP 프레임워크에서 제공하는 인터페이스이며, 현재 실행 중인 메서드에 대한 정보를 제공하고 메서드의 실행을 제어할 수 있는 기능을 제공합니다.

      ExposeInvocationInterceptor는 특수한 AOP 인터셉터로서, 현재 실행 중인 MethodInvocation 객체를 스레드 로컬 객체에 저장합니다.

      따라서 ExposeInvocationInterceptor.currentInvocation() 메서드는 언제 어디서든 현재 실행 중인 MethodInvocation 객체에 액세스할 수 있습니다.

       

      다음은 ExposeInvocationInterceptor.currentInvocation() 메서드를 사용하는 예제 코드입니다.

      public class MyAdvice {
      
          @AroundAdvice
          public Object handle(ProceedingJoinPoint joinPoint) throws Throwable {
              // 현재 실행 중인 MethodInvocation 객체
              MethodInvocation invocation = ExposeInvocationInterceptor.currentInvocation();
      
              // MethodInvocation 객체의 타겟 객체
              Object target = invocation.getThis();
      
              // MethodInvocation 객체의 메서드
              Method method = invocation.getMethod();
      
              // ...
      
              return joinPoint.proceed();
          }
      
      }

       

      다른 쓰레드에서 실행될 때, methodInvocation 을 복사해 주는 용도로 사용합니다

       

      구현

        @Override
        public Object handle(ProceedingJoinPoint proceedingJoinPoint, TimeLimiter timeLimiter,
            String methodName) throws Throwable {
      
          final MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
      
          CompletionStage<Object> completionStage =
            timeLimiter.executeCompletionStage(scheduledExecutorService, () ->
              CompletableFuture.supplyAsync(() -> {
                try {
      
                  // thread 간 methodInvocation 복사
                  @SuppressWarnings("unchecked")
                  final var threadLocal = (ThreadLocal<MethodInvocation>)invocationField.get(null);
                  threadLocal.set(mi);
                  return proceedingJoinPoint.proceed();
                } catch (CompletionException e) {
                  throw e;
                } catch (Throwable e) {
                  throw new CompletionException(e);
                }
              }, scheduledExecutorService));
      
          // 예외 처리
          try {
            return completionStage.toCompletableFuture().join();
          } catch (CompletionException e) {
            Throwable cause = e.getCause();
            
            if (cause instanceof TimeoutException) {
              throw e;
            } else {
              throw cause;
            }
          }
        }

      1) 쓰레드 간 methodInvocation 을 복사해 줍니다

      2) CompletionException Exception 을 잡아서 전달해줍니다

       

       

      'Spring > CircuitBreaker' 카테고리의 다른 글

      Resilience4j 관련  (0) 2023.09.24
      Micrometer 관련  (0) 2023.09.24

      댓글

    Designed by Tistory.