본문 바로가기

Spring/API Gateway

3) Spring Boot API Gateway - 3부 CircuitBreaker설정

반응형

앞에 api gateway route 설정과 LB 설정을 하고 어떤식으로 작동하는지에 대해 알아 보았다.

이번에는 gateway circuitbreak 기능에 대해 공부해보자.

 

circuitbreak란 무엇일까?

간단하게 말하면 api가 route 하는 과정에서 해당 api의 응답이 느려져서( timeout) , 에러가 나는( exception) 

중에 계속해서 해당 api 를 불러서 여러 가지 문제를 확산( 해당 api 가 DB connection을 점유한다든지 , 서버 리소스를 잠식 한다든지)

시키는 문제를 방지 하고자 . api gateway에서 특정 조건이 (호출수 대비 실패율) 만족되면 gateway에 api를 호출하지 않고 

error 처리를 하는것을 말한다.

 

이렇게 처리를 함으로써 서버에서 장애 확산을 방지하고 client에서는 빠른 응답을 받아 에러에 대한 빠른 처리가 가능해진다.

 

그럼 어떤식으로 circuitbreak 설정을 하는지 알아보자.

아래는 api gateway의 applicaton.yml 이다.

spring:
  application:
    name: apigateway-service
  cloud:
    circuitbreaker:
      resilience4j:
        enabled: true
    gateway:
      httpclient:
        connect-timeout: 2000
        response-timeout: 2s
      routes:
        - id: auth-service
          uri: lb://AUTH-SERVICE
          predicates:
            - Path=/auth-service/**
          filters:
            - name: CircuitBreaker
              args:
                  name: testCircuitBreaker
                  fallbackUri: forward:/fallback/authFaliure
                  

resilience4j:
  timelimiter:
    configs:
      default:
        cancelRunningFuture: false
        timeoutDuration: 5s
  circuitbreaker:
    configs:
      default:
        registerHealthIndicator: true # actuator를 통해 circuitbraker 상태를 확인하기 위해 설정
        minimumNumberOfCalls: 10 # Circuit Breaker가 에러 비율 또 slow call 비율을 계산하기 전에 요구되는 최소한의 요청 수
        failureRateThreshold: 50  # 에러 비율 (퍼센트)로 해당 값 이상으로 에러 발생시 서킷이 Open 된다.
        waitDurationInOpenState: 10s  # 서킷의 상태가 Open에서 Half-open으로 변경되기 전에 Circuit Breaker가 기다리는 시간
    instances:
      testCircuitBreaker:
        baseConfig: default

위 설정에서 보면 route하는 부분에 어떤 circuitbreaker 사용하지를 정하고 해당 circuitbreak의 fallback 처리를 어떻게 할지 정한다.

 

실제 resilience4j의 설정은 아래 부분에 있다. testCircuitBreaker를 만들고 해당 설정은 default 설정을 따르게 해놨다.

이제 설정이 잘 적용 되었는지 확인을 위해 actuator를 설정을 해주면 된다.

아래는 application.yml의 전체 모습이다.

server:
  port: 8080
spring:
  application:
    name: apigateway-service
  cloud:
    circuitbreaker:
      resilience4j:
        enabled: true
    gateway:
      httpclient:
        connect-timeout: 2000
        response-timeout: 2s
      routes:
        - id: auth-service
          uri: lb://AUTH-SERVICE
          predicates:
            - Path=/auth-service/**
          filters:
            - name: CircuitBreaker
              args:
                  name: testCircuitBreaker
                  fallbackUri: forward:/fallback/authFaliure

eureka:
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:8761/eureka
  instance:
    instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}

resilience4j:
  timelimiter:
    configs:
      default:
        cancelRunningFuture: false
        timeoutDuration: 5s
  circuitbreaker:
    configs:
      default:
        registerHealthIndicator: true # actuator를 통해 circuitbraker 상태를 확인하기 위해 설정
        minimumNumberOfCalls: 10 # Circuit Breaker가 에러 비율 또 slow call 비율을 계산하기 전에 요구되는 최소한의 요청 수
        failureRateThreshold: 50  # 에러 비율 (퍼센트)로 해당 값 이상으로 에러 발생시 서킷이 Open 된다.
        waitDurationInOpenState: 10s  # 서킷의 상태가 Open에서 Half-open으로 변경되기 전에 Circuit Breaker가 기다리는 시간
    instances:
      testCircuitBreaker:
        baseConfig: default

management:
  endpoints:
    web:
      exposure:
        include: "*"
  endpoint:
    shutdown:
      enabled: true
    health:
      show-details: always # actuator에서 디테일한 정보들을 확인하기 위해 설정
  health:
    circuitbreakers:
      enabled: true # actuator를 통해 circuitbraker 상태를 확인하기 위해 설정

 

이제 gateway 서버를 재실행하고 actuator로 circuitBreak의 상태를 보자.

서버가 재실행되고 

http://localhost:8080/actuator/health/circuitBreakers 를 호출하면 아래와 같은 정보가 표시 된다.

{
  "status": "UP",
  "details": {
    "testCircuitBreaker": {
      "status": "UP",
      "details": {
        "failureRate": "-1.0%",
        "failureRateThreshold": "50.0%",
        "slowCallRate": "-1.0%",
        "slowCallRateThreshold": "100.0%",
        "bufferedCalls": 0,
        "slowCalls": 0,
        "slowFailedCalls": 0,
        "failedCalls": 0,
        "notPermittedCalls": 0,
        "state": "CLOSED"
      }
    }
  }
}

현재 circuitbreak의 상태는 close 상태로 api에 문제가 없어 닫혀 있다.

이제 api의 문제를 일으켜( exception유발 또는 timeout발생) circuitbreak를 작동시켜보자.

auth api에 아래와 같이 변경해서 테스트해보자.

@RequestMapping("/auth-service/login")
    public String authlogin(HttpServletRequest request){
//        throw new RuntimeException("failed");
        try{
            Thread.sleep(5000);
        }catch(Exception e){
            e.printStackTrace();
        }
        log.info("request server port : {}" , request.getServerPort());
        return "TEST-OK";
    }
{
  "status": "UP",
  "details": {
    "testCircuitBreaker": {
      "status": "UP",
      "details": {
        "failureRate": "-1.0%",
        "failureRateThreshold": "50.0%",
        "slowCallRate": "-1.0%",
        "slowCallRateThreshold": "100.0%",
        "bufferedCalls": 4,
        "slowCalls": 0,
        "slowFailedCalls": 0,
        "failedCalls": 4,
        "notPermittedCalls": 0,
        "state": "CLOSED"
      }
    }
  }
}

auth timeout이 발생하게 sleep을 걸은 api를 4번 호출했더니 위에  circuitbreak 상태의 failcalls 가 4로 늘었다.

요기서 더 실패를 발생시키면 어떻게 되는지 보자.

{
  "status": "UNKNOWN",
  "details": {
    "testCircuitBreaker": {
      "status": "CIRCUIT_OPEN",
      "details": {
        "failureRate": "100.0%",
        "failureRateThreshold": "50.0%",
        "slowCallRate": "0.0%",
        "slowCallRateThreshold": "100.0%",
        "bufferedCalls": 10,
        "slowCalls": 0,
        "slowFailedCalls": 0,
        "failedCalls": 10,
        "notPermittedCalls": 7,
        "state": "OPEN"
      }
    }
  }
}

failRate이 100프로 되면 state 이 OPEN으로 변경되고 실제 api를 다시 호출하게 되면 앞에서 circuitebreak 작동하지 않는 상태에 

5초동안 로딩이 걸리는부분 없이 바로 fallback url로 리턴되는것이 확인된다.

 

fallback 에 대한 설정은 각자 상황에 맞게 처리를 하면 될것 같다.

여기까지 간단히 circuitebreak 설정 및 테스트 방법에 대해 알아보았다. 

 

참고: https://github.com/devraccon/gateway.git