본문 바로가기

Spring

Spring cloud circuit breaker fallbackUri 사용

728x90
반응형

서비스 하나를 (fallback 날릴) 만들어서 gateway instance로 추가해준다.

application.yml

spring:
  application:
    name: scg-gateway
  cloud:
    gateway:
      routes:       
        - id: test-svc
          uri: lb://TEST-SVC
          predicates:
          - Path=/testSvc/**
          filters:
            - name: CircuitBreaker
              args:
                name: testCircuitBreaker
                fallbackUri: forward:/fallback

resilience4j.circuitbreaker:
  configs:
    default:
      failureRateThreshold: 50
      slowCallRateThreshold: 100
      slowCallDurationThreshold: 60000
      permittedNumberOfCallsInHalfOpenState: 4
      maxWaitDurationInHalfOpenState: 1000
      slidingWindowType: COUNT_BASED
      slidingWindowSize: 10
      minimumNumberOfCalls: 10
      waitDurationInOpenState: 10000
      registerHealthIndicator: true
      recordExceptions: java.util.concurrent.TimeoutException
      eventConsumerBufferSize: 10

  instances:
     test-svc:
      baseConfig: default

resilience4j.timelimiter:
  configs:
    default:
      timeoutDuration: 3s

management:
  health:
    circuitbreakers:
      enabled: true
  endpoint:
    health:
      showDetails: always
    gateway:
      enabled: true

  endpoints:
    web:
      exposure:
        include: '*'

  metrics:
    enable:
      resilience4j:
        circuitbreaker:
          calls: true

gateway 라우트 설정 중 filters에 CircuitBreaker를 등록했다.

args Name은 작성한 대로 /actuator/health 에서 확인할 수 있다.

fallbackUri에 fallback시 호출할 Uri를 작성해준다. 

주의할 점은 forward://이 아니라 forward:/ <-- 하나다.

 

recordExceptions :  서킷을 잡을 에러를 등록했다. 제일 만만하게 테스트할 수 있는게 타임아웃이다.

resilience4j.timelimiter.configs.default.timeoutDuration: 3s

3초 이후에 오면 fallback을 호출하도록 설정했다.

 


Test서비스의 TestController.java

@GetMapping("/test")
public String test(@RequestParam(required = false)String id) throws InterruptedException {
    int randomNo = new Random().nextInt();
    String result = randomNo % 2 == 0? testService.test(): testService.fail();
    return result;
}

랜덤하게 성공과 실패를 호출하도록 한다.

 

 

TestService.java

@Service
public class TestService {

    public String test() {
        return "hi";
    }

    public String fail() throws InterruptedException {
        Thread.sleep(5000);
        return "fail";
    }
}

fail이 호출될 경우 5초 sleep을 준다.

게이트웨이 time duration이 3초 이므로 TimeoutException이 발생한다.

 


Gateway FallbackController.java

@RestController
public class FallbackController {

    @GetMapping("/fallback")
    public Mono<Void> fallback(ServerWebExchange exchange) {
        Throwable t = exchange.getAttribute(ServerWebExchangeUtils.CIRCUITBREAKER_EXECUTION_EXCEPTION_ATTR);
        if(t instanceof TimeoutException) {
            throw new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, "Service Timeout");

        } else if (t instanceof CallNotPermittedException) {
            throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE, "Service is not available");
        }
        return null;
    }
}

Exception을 잡아서 따로 처리할 수도 있다.

CallNotPermittedException은 서킷이 오픈되었을 때 발생하는 Exception이다.

 


서버를 올리고 처음 호출했을 때 서킷의 상태

health체크하는 방법은 아래링크

https://tweety1121.tistory.com/entry/Spring-circuitbreaker-actuator-health-check-%EC%84%A4%EC%A0%95

 

Spring circuitbreaker actuator health check 설정

Gradle implementation "org.springframework.cloud:spring-cloud-starter-circuitbreaker-reactor-resilience4j" implementation 'org.springframework.boot:spring-boot-starter-actuator' CircuitBreaker Confi..

tweety1121.tistory.com

 

계속 호출하면서 서킷이 오픈되면

 

서킷의 상태가 바뀐다.

 

fallback컨트롤러에서 정의한 상태대로 리턴된다.

{
    "timestamp": "2022-02-04T12:50:55.614+00:00",
    "path": "/testSvc/test",
    "status": 503,
    "error": "Service Unavailable",
    "message": "Service is not available"
}
728x90
반응형