Refactoring: Spring Circuit Breaker 적용 - takeoff-26/logistics-service GitHub Wiki

Spring Circuit Breaker 적용


서킷 브레이커는 장애를 방지하기 위한 패턴으로 장애 발생 지점을 감지하고 실패하는 요청을 계속적으로 보내지 않도록 방지하는 패턴이다.

서킷 브레이커는 3개의 상태를 통해 관리가 된다.

Closed - 요청의 실패율이 설정한 실패 임계치보다 낮은 상태

Open - 요청의 실패율이 설정한 실패 임계치보다 높은 상태

Half-Open - Open 상태 이후 일정 시간이 지난 상태이며, 이후 요청의 성공/실패 상태에 따라 Closed/Open 상태로 변경된다.

실패 임계치를 정할 때는 2가지 기준으로 요청이 실패했다는 것을 정의할 수 있다.

slow call - 설정한 시간보다 오래 걸린 요청으로 지연된 요청

failure call - 요청이 실패하거나 에러 응답을 받은 요청이다.


@Retry(name = SLACK_RETRY, fallbackMethod = FALL_BACK_RESPONSE)
@CircuitBreaker(name = SLACK_CIRCUIT_BREAKER, fallbackMethod = FALL_BACK_RESPONSE)
public void sendSlackMessageToChannel(String message, String channel){
    try{

        ChatPostMessageRequest request = ChatPostMessageRequest.builder()
            .channel(channel)
            .text(message)
            .build();
        if (message.equals("서킷 브레이커")) {
            throw new IllegalArgumentException("서킷 브레이커 테스트");
        }

        methodsClient.chatPostMessage(request);

        log.info("Slack " + channel + " 에 메시지 보냄");
    } catch (SlackApiException | IOException e) {
        log.error(e.getMessage());
        throw BusinessException.from(SlackErrorCode.SLACK_ERROR);
    }
}
private void slackRequestFail(String message, String channel, Throwable t) {
    log.error("slack 서비스 장애 발생 {}", t.getMessage());
    log.error("전송하려던 메시지: {}", message);
    log.error("전송해야할 채널 {}", channel);
}

image


#서킷브레이커 설정

#서킷브레이커 설정
resilience4j:
  circuitbreaker:
    configs:
      default:
        #실패 요청(failure call) 임계치 설정 요청 실패가 50 이상시 OPEN 상태로 전환
        failure-rate-threshold: 50
        #지연 요청(slow call)의 임계치 설정 기본값이 100이지만 지연 요청이 100일 때 서버 스레드를
        #전부 점유할 수 있으므로 100 보다 적게 설정
        slow-call-rate-threshold: 80
        #지연 요청이라고 판단할 시간 설정 5초로 지정 했습니다.
        slow-call-duration-threshold: 5s
        #Half-Open에서 Open,Close 상태 전환을 판단할 요청 개수
        #ex)3개 요청이 전부 성공시 Closed, 하나라도 실패시 Open
        permitted-number-of-calls-in-half-open-state: 3
        #기본 값은 0으로 위 설정한 permitted 요청 개수가 모두 올 때까지 Half_Open으로 대기
        max-wait-duration-in-half-open-state: 0
        #요청 시간 기반과 요청 요청 개수 기반으로 슬라이딩 윈도우 타입을 설정
        sliding-window-type: COUNT_BASED
        #사이즈를 10으로 요청 개수가 10개가 될 때마다 집계 되도록 설정
        sliding-window-size: 10
        #지연 요청과 실패 요청이 계산되는 최소 요청 수
        #ex) 슬라이딩 윈도우 사이즈 10이라면 아래 값이 슬라이딩 윈도우 사이즈보다 낮을 때
        # -> 낮은 수의 요청이 들어왔을 때 해당 요청의 지연, 실패 요청을 계산해 상태 전환을 판단.
        #ex) 아래 설정 값이 슬라이딩 윈도우 사이즈보다 클 때는 윈도우 사이즈만큼 계산된다.
        minimum-number-of-calls: 10
        #Open에서 최소 5초가 지나고 요청이 왔을 때 Half-Open이 되도록 설정
        wait-duration-in-open-state: 5s
    instances:
      shboard-circuit-breaker:
        base-config: default

요약하면 다음과 같다

요청이 10개 이상 있어야 서킷 브레이커가 활성화.

실패율 50%, 지연율 80% 이상이면 OPEN 상태로 전환.

OPEN 상태에서 5초 후 Half-Open 상태로 변경.

Half-Open 상태에서 3개 요청을 테스트한 후 성공 여부에 따라 다시 CLOSED or OPEN으로 전환.

이런 흐름이다.

"slackService": {
      "failureRate": "50.0%",
      "slowCallRate": "0.0%",
      "failureRateThreshold": "50.0%",
      "slowCallRateThreshold": "80.0%",
      "bufferedCalls": 10,
      "failedCalls": 5,
      "slowCalls": 0,
      "slowFailedCalls": 0,
      "notPermittedCalls": 0,
      "state": "OPEN"
    }

위와 같이 임계점에 따른 상태가 변환된다.

서킷 브레이커를 통해 요청에 대해 에러 상황에서도 안정적인 서비스를 유지할 수 있게 되었다. feign client 같은 요청은 비즈니스적 답을 담고 있기 때문에 특별한 요구 사항이 아니라면 값이 제대로 들어와야 하기 때문에 Retry나 로그를 남기며 상태에 따른 추적이나 사용자에 대해 알리기 위한 값을 설정하는 방면에서 사용하는 것이 알맞는다 생각한다.