본문 바로가기
IT/Spring Cloud

Spring Cloud 시리즈 3 - Resilience4j #2

by 최고영회 2023. 5. 25.
728x90
반응형
SMALL

Resilience4j 의 첫번째 포스팅에서는 Retry 와 Circuit Breaker 에 대해서 알아봤다. 

https://kimyhcj.tistory.com/entry/Spring-Cloud-%EC%8B%9C%EB%A6%AC%EC%A6%88-3-Circuit-Breaker-Resilience4j-1

 

Spring Cloud 시리즈 3 - Circuit Breaker (Resilience4j #1)

https://kimyhcj.tistory.com/entry/Spring-Cloud-%EC%8B%9C%EB%A6%AC%EC%A6%88-3-Circuit-Breaker-Hystrix Spring Cloud 시리즈 3 - Circuit Breaker (Hystrix) Circuit Breaker Pattern Software system 이 네트워크상 서로 다른 소프트웨어를 원격으

kimyhcj.tistory.com

 

Rate Limiter 

official doc: https://resilience4j.readme.io/docs/ratelimiter

Rate limiting 은 API 규모 확장에 대비하고 서비스의 고가용성과 안정성을 확립하기 위해 필요한 기술이다. 

제한치를 넘어간 것을 감지했을 때의 동작이나 제한할 요청 타입과 관련된 광범위한 옵션들을 제공한다.

간단히 제한치를 넘어선 요청을 거부하거나, 큐를 만들어 나중에 실행할 수도 있다. 

 

<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-ratelimiter</artifactId>
</dependency>

필요한 dependency 추가 

 

server:
  port: 8081
resilience4j:
  ratelimiter:
    instances:
      getProductPrice:
        limitForPeriod: 5 # cycle 동안 호출할 수 있는 횟수
        limitRefreshPeriod: 1s # cycle 이 가지는 주기, cycle 주기가 끝나면 호출 가능 횟수는 다시 리셋됨, default: 500ns
        timeoutDuration: 0 # 호출 thread 가 rateLimit 에 대해서 접근 허가를 얻기 위해 대기하는 시간, default: 5s

.yaml 에 필요한 정보를 설정한다. (ex. 1초에 5개의 요청만 처리 가능하도록 설정)

 

 

@RestController @RequiredArgsConstructor @Slf4j
public class TestController {
    final TestService service;

    @RateLimiter(name = "getProductPrice", fallbackMethod = "failedGetProductPriceRl")
    @GetMapping("/product/{productId}")
    public ResponseEntity<String> getProductInfo(@PathVariable String productId) {
        return ResponseEntity.ok(service.getProductPrice(productId));
    }
    public ResponseEntity<String> failedGetProductPriceRl(Throwable t) {
        log.error("[RateLimit] Failed get product price");
        return ResponseEntity.internalServerError().body("잠시 후 다시 시도해 주시기 바랍니다. ");
    }
}

@Service 에 해도 되는데 ResponseEntity 에서 500 error 를 return 해 보고 싶어서 Controller 에 바로 @RateLimiter 를 적용해 봤다.

 

jmeter 를 이용해 1초에 10번의 요청을 보내보면 위 그림 처럼 5개만 성공 하고 5개는 실패하는 것을 확인할 수 있다.

timeoutDuration 을 300ms 로 조정해 보면 어떻게 될까?

 

호출 thread 가 rateLimit 을 얻기 위해 300ms 를 기다리면서 10개의 요청 중 9개 요청이 처리되는 것을 확인할 수 있다.

 

 

 

Time Limiter

official doc: https://resilience4j.readme.io/docs/timeout

Hystrix 의 timeoutInMilliseconds 와 동일하다고 볼 수 있다.p.s Time Limiter 는 CompletionStage type 을 return 해야 한다.

<dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-timelimiter</artifactId>
</dependency>

필요한 dependency 추가

 

server:
  port: 8081
resilience4j:
  timelimiter:
    instances:
      getProductPrice:
        timeoutDuration: 2s

application.yaml 에 관련 설정 추가 (ex. 2초)

 

@TimeLimiter(name = "getProductPrice", fallbackMethod = "failedGetProductPriceTl")
@GetMapping("/product/time/{productId}")
public CompletableFuture<ResponseEntity<String>> getProductInfoTime(@PathVariable String productId) {
    return CompletableFuture.supplyAsync(() -> ResponseEntity.ok(service.getProductPrice(productId)));
}

public CompletableFuture<ResponseEntity<String>> failedGetProductPriceTl(String productId, Throwable t) {
    log.error("[TimeLimit] Failed get product price");
    return CompletableFuture.supplyAsync(() -> ResponseEntity.status(HttpStatus.REQUEST_TIMEOUT).body("시간초과"));
}

HTTP Status REQUEST_TIMEOUT 를 return 하기 위해 controller 에 적용

 

external api 에서 sleep 3초 주고 테스트 해 보면 위와 같이 예상하는대로 동작한다.

 

 

Bulkhead

official doc: https://resilience4j.readme.io/docs/bulkhead

 <dependency>
    <groupId>io.github.resilience4j</groupId>
    <artifactId>resilience4j-bulkhead</artifactId>
</dependency>

필요한 dependency 추가

 

resilience4j:
  bulkhead:
    instances:
      bulkTest:
        maxConcurrentCalls: 5 # bulkhead 에서 최대로 허용할 병렬 실행 수
        #maxWaitDuration: 0 # bulkhead 가 포화 상태일 때 진입하려는 thread 를 blocking 할 최대 시간

application.yaml 에 필요한 설정 정보 추가 

 

@Bulkhead(name = "bulkTest", fallbackMethod = "failedGetProductPriceBk")
public ResponseEntity<String> getProductPrice() {
    return new ResponseEntity<>(rest.getForObject("http://localhost:8080/product/123", String.class), HttpStatus.OK);
}
public ResponseEntity<String> failedGetProductPriceBk(Throwable t) {
    log.error("[Bulkhead] Failed get product price");
    return new ResponseEntity<>("bulk error", HttpStatus.CONFLICT);
}

bulk exception 발생 시 409 Error return 하도록 fallback method 정의

 

jmeter 에서 동시 접속 10명으로 설정하고 테스트 진행

 

 

총 10개의 동시 요청에 대하여 5개만 성공하고 나머지 5개는 409 conflict 오류 발생 확인 

 

728x90
반응형
LIST