백앤드 개발일지/웹, 백앤드

[CircuitBreaker] 게이트웨이에서 서킷 브레이커 설정시 주의할점

giron 2024. 6. 30. 02:51
728x90

게이트웨이를 운영하던중 첫 적립 api를 호출했을때 아래의 예외가 터졌다.

java.util.concurrent.TimeoutException: Did not observe any item or terminal signal within 1000ms in 'contextWriteRestoringThreadLocals' (and no fallback has been configured)

게이트웨이 구성은 spring cloud gateway로 구성되어있고 아래 zipkin에서 보면 response-time이 1s가 넘어가자 exception을 던지는 것으로 확인했다. 

 

zipkin

다만 gateway에서 response-timeout설정을 1s보다 길게 잡아두었는데 1000ms에서 예외가 터지는게 의아했다.

원인 찾기

1. 일반적인 response-timeout으로 예외가 발생하면 아래처럼 예외가 발생해야하는데 예외 내용도 달랐다.

org.springframework.web.server.ResponseStatusException: 504 GATEWAY_TIMEOUT "Response took longer than timeout: PT2S"

2. 해당 예외가 터질때마다 circuitBreaker의 fail count가 올라갔다. 따라서 해당 exception이 circuit breaker에 걸어둔 exception 조건에 맞는 예외일 것이라고 가정했다 실제 circuit breaker를 제거한 상태에서는 아무리 지연되어도 response-timeout내에서는 정상 응답을 받았다.

결과

원인으로는 circuit breaker를 추가하면 timelimiter의 default timeout기간이 1s이다. 따라서 해당 설정이 우선순위가 되어 http-client의 response timeout이 1s보다 길더라도 timelimiter에 걸려서 예외가 발생한 것이다.

public class TimeLimiterConfig implements Serializable {
    private static final long serialVersionUID = 2203981592465761602L;
    private static final String TIMEOUT_DURATION_MUST_NOT_BE_NULL = "TimeoutDuration must not be null";
    private Duration timeoutDuration = Duration.ofSeconds(1L);
    private boolean cancelRunningFuture = true;

추후 조치

timelimiter timeout > metadata timeout > httpclient timeout 우선순위이므로 timelimiter의 timeout을 httpclient timeout보다 길게 잡으면
metadata timeout > httpclient timeout > timelimiter timeout순서가 되도록 timelimiter timeout을 httpclient timeout보다 길게 설정하면 좋을 것 같습니다. (circuit을 붙이고 때더라도 spring gateway에서 설정한 timeout을 일정하게 유지하기 위함)

spring:
  cloud:
    gateway:
      httpclient:
        connect-timeout: 1000
        response-timeout: 1s # 여기서는 1s가능.
    routes:
      - id: api-server
        uri: http://localhost:8081
        filters:
          - name: CircuitBreaker
            args:
              name: tmpCircuitBreaker
              fallbackUri: forward:/fallback/tmp
        metadata:
          connect-timeout: 1000
          response-timeout: 1000 # 1s로 적으면 1초 넘어도 exception안터짐
resilience4j:
  timelimiter:
    configs:
      default:
        timeout-duration: 10s
        cancel-running-future: false

추가 사항

timelimiter의 timeout-duration은 slow-call-duration보다 반드시 커야한다. 안그러면 서킷브레이커에 집계되기 전에 timeout예외가 터지기때문이다. 

slow-call-duration <= metadata timeout <= httpclient timeout < timelimiter timeout

Reference

https://docs.spring.io/spring-cloud-gateway/docs/3.0.2/reference/html/#route-metadata-configuration

 

Spring Cloud Gateway

This project provides an API Gateway built on top of the Spring Ecosystem, including: Spring 5, Spring Boot 2 and Project Reactor. Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to them

docs.spring.io

https://medium.com/@im_zero/spring-cloud-gateway-circuit-breaker-time-limiter-5e3c26a62b4c

728x90