ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Circuit Breaker #2. 데모동작
    개발공부/서버 네트워크 2023. 1. 20. 17:48
    {
          "circuitBreakerName": "circuit-test-70000",
          "type": "ERROR",
          "creationTime": "2023-01-25T16:12:24.036+09:00[Asia/Seoul]",
          "errorMessage": "java.lang.RuntimeException: failed!!!!",
          "durationInMs": 0
        },
        {
          "circuitBreakerName": "circuit-test-70000",
          "type": "SUCCESS",
          "creationTime": "2023-01-25T16:12:25.651+09:00[Asia/Seoul]",
          "durationInMs": 0
        },
        {
          "circuitBreakerName": "circuit-test-70000",
          "type": "SUCCESS",
          "creationTime": "2023-01-25T16:12:27.060+09:00[Asia/Seoul]",
          "durationInMs": 0
        },
        {
          "circuitBreakerName": "circuit-test-70000",
          "type": "STATE_TRANSITION",
          "creationTime": "2023-01-25T16:12:27.060+09:00[Asia/Seoul]",
          "stateTransition": "HALF_OPEN_TO_CLOSED"
        },
        {
          "circuitBreakerName": "circuit-test-70000",
          "type": "SUCCESS",
          "creationTime": "2023-01-25T16:12:28.456+09:00[Asia/Seoul]",
          "durationInMs": 0
        },

    #Circuit Breaker 종류

    • Netflix Hystrix
      • 넷플릭스에서 만든 라이브러리로 MSA 환경에서 분산된 서비스간 통신이 원활하지 않은 경우에 각 서비스가 장애 내성과 지연 내성을 갖게하도록 도와주는 라이브러리
      • 현재는 지원종료 상태이며 Resilience4j 가 권장되는 상태
    • Resilience4j
      • Resilience4j 는 Netflix Hystrix 로 부터 영감을 받은 Fault Tolerance Libray
      • 사용하기 가볍고 다른 라이브러리 의존성이 없음
      • Java 전용으로 개발된 경량화된 Fault Tolerance Libray
      • Spring Boot 전용은 아님
      • 대표적인 패턴
        • Retry
          • 실패한 실행을 짧은 지연을 가진 후 재시도합니다.
        • Circuit Breaker
          • 실패한 실행에 대해서 또 다른 시도가 들어올 때 바로 실패 처리합니다.

    #Circuit Breaker 적용

    ##시나리오

    • A서버에서 B서버에 http call
      • A서버 -> circuit breaker -> B서버
      • A서버에 서킷브레이커 적용
    • 컨트롤러 별로 설정하기보다는 서비스 레이어의 B서버를 호출하는 메서드별로 설정하는 것을 권장

    ##프로젝트 환경

    JDK 1.8

    spring boot 2.2.1

    Resilience4j version 1.7.0

     

    ##예제

    #01. spring boot에서 지원하는 dependency pom.xml에 추가

    <!-- resilience4j -->
      <dependency>
          <groupId>io.github.resilience4j</groupId>
          <artifactId>resilience4j-spring-boot2</artifactId>
          <version>1.7.0</version>
      </dependency>
     
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-actuator</artifactId>
      </dependency>
     
      <dependency>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-aop</artifactId>
      </dependency>

    #02. application.properties에 서킷브레이커 적용을 위한 설정값 추가

    ##서킷브레이커 적용을 위한 설정값
    #1) retry
    #최대 재시도 수
    resilience4j.retry.configs.default.max-attempts=5
    #재시도 할 때마다 기다리는 고정시간 (1초[1000ms], 기본값 : 0.5[500ms])
    resilience4j.retry.configs.default.wait-duration=5000
    #실패로 기록되는 에러 클래스 리스트. 즉, 재시도돼야 하는 에러 클래스의 리스트. empty일 경우 모든 에러 클래스를 재시도 (기본값 : empty)
    #resilience4j.retry.configs.default.retry-exceptions[0]=org.springframework.web.client.HttpServerErrorException
    #resilience4j.retry.configs.default.retry-exceptions[1]=java.io.IOException
    #무시돼야 하는 에러 클래스 리스트. 즉, 재시도 되지 않아야 할 에러 클래스 리스트. (기본값 : empty)
    #resilience4j.retry.configs.default.ignore-exceptions[0]=java.util.NoSuchElementException
     
    #retry name : retry-test-3000
    #기본 config 지정. ex) retry.configs.{default}
    resilience4j.retry.instances.retry-test-3000.base-config=default
    resilience4j.retry.instances.retry-test-3000.wait-duration=3000
     
    resilience4j.retry.instances.retry-db-select-4000.base-config=default
    resilience4j.retry.instances.retry-db-select-4000.wait-duration=4000
    resilience4j.retry.instances.retry-db-select-5000.base-config=default
    resilience4j.retry.instances.retry-db-select-5000.wait-duration=5000
     
    #2) circuitbreaker
    #default : 기본 config명
    resilience4j.circuitbreaker.configs.default.register-health-indicator=true
    #sliding window 타입을 결정. COUNT_BASED - slidingWindowSize만큼의 마지막 call들이 기록되고 집계 / TIME_BASED - 마지막 slidingWindowSize초 동안의 call들이 기록되고 집계
    resilience4j.circuitbreaker.configs.default.sliding-window-type=TIME_BASED
    #CLOSED 상태에서 집계되는 슬라이딩 윈도우 크기를 설정. (기본값: 100)
    resilience4j.circuitbreaker.configs.default.sliding-window-size=10
    #최소한 호출을 10번을 기록해야 실패 비율을 계산가능
    resilience4j.circuitbreaker.configs.default.minimum-number-of-calls=10
    #임계값을 백분율로 설정. 서킷브레이커는 호출에 걸리는 시간이 slowCallDurationThreshold보다 길면 느린 호출로 간주.
    #해당 값을 넘어갈 시 서킷브레이커는 OPEN상태로 전환되며, 이때부터 호출을 차단한다 (기본값: 100)
    resilience4j.circuitbreaker.configs.default.slow-call-rate-threshold=100
    #호출에 소요되는 시간이 설정한 임계치보다 길면 느린 호출로 계산. -> 응답시간이 느린것으로 판단할 기준 시간 (60초, 1000 ms = 1 sec) (기본값: 60000[ms])
    resilience4j.circuitbreaker.configs.default.slow-call-duration-threshold=60000
    #실패비율 임계치를 백분율로 설정하여 해당 값을 넘어갈 시 서킷브레이커는 OPEN 상태로 전환되며, 이때부터 호출 차단 (기본값 : 50)
    resilience4j.circuitbreaker.configs.default.failure-rate-threshold=50
    #HALF_OPEN 상태일 때, OPEN/CLOSE 여부를 판단하기 위해 허용할 호출 횟수를 설정 수 (기본값: 10)
    resilience4j.circuitbreaker.configs.default.permitted-number-of-calls-in-half-open-state=10
    #서킷의 상태가 OPEN 에서 HALF-OPEN 으로 변경되기전에 서킷브레이커가 기다리는 시간 [s] (60초, 1000 ms = 1 sec) (기본값: 60000[ms])
    resilience4j.circuitbreaker.configs.default.wait-duration-in-open-state=10s
     
    #circuitbreaker name : circuit-test-70000
    #기본 config 지정. ex) circuitbreaker.configs.{default}
    resilience4j.circuitbreaker.instances.circuit-test-70000.base-config=default
    #응답시간이 느린것으로 판단할 기준 시간 [ms]
    resilience4j.circuitbreaker.instances.circuit-test-70000.slow-call-duration-threshold=70000
     
    resilience4j.circuitbreaker.instances.circuit-db-select-200.base-config=default
    resilience4j.circuitbreaker.instances.circuit-db-select-200.slow-call-duration-threshold=200
    resilience4j.circuitbreaker.instances.circuit-db-select-300.base-config=default
    resilience4j.circuitbreaker.instances.circuit-db-select-300.slow-call-duration-threshold=300

    #03. CircuitBreaker 적용

    - 실패한 실행에 대해서 또 다른 시도가 들어올 때 바로 실패 처리

    #Resilience4j 상수
    
    // Resilience4j 상수 선언 (서킷브레이커 관련 모듈 네임 매핑 용도)
    public final class Resilience4jCode {
     
        public static final String RETRY_TEST_3000 = "retry-test-3000";
        public static final String RETRY_DB_SELECT_4000 = "retry-db-select-4000";
        public static final String RETRY_DB_SELECT_5000 = "retry-db-select-5000";
        public static final String CIRCUIT_TEST_70000 = "circuit-test-70000";
        public static final String CIRCUIT_DB_SELECT_200 = "circuit-db-select-200";
        public static final String CIRCUIT_DB_SELECT_300 = "circuit-db-select-300";
    }
    #Controller
    
    @RestController
    @RequiredArgsConstructor
    @RequestMapping("/circuitbreaker")
    public class CircuitBreakerController {
        private final CircuitBreakerService circuitBreakerService;
     
        //호출 Method에 name(yml에 선언한 값 -> 상수(=Resilience4jCode)), fallbackMethod: fallbackMethod method명을 지정
        @GetMapping("/hello")
        public String hello() {
            return circuitBreakerService.getCircuitBreaker();
        }
    }
    #Service
    
    @Slf4j
    @Service
    public class CircuitBreakerService {
     
        @CircuitBreaker(name = Resilience4jCode.CIRCUIT_TEST_70000, fallbackMethod = "getCircuitBreakerFallback")
        public String getCircuitBreaker() {
            runtimeException();
            return "hello world!";
        }
     
        private void runtimeException() {
            throw new RuntimeException("failed");
        }
        private String getCircuitBreakerFallback(Throwable t) {
            return "getCircuitBreakerFallback! exception type: " + t.getClass() + "exception, message: " + t.getMessage();
        }
     
    }

    [실행결과]

    1. 포스트맨으로 A서버의 api요청 (해당 서비스는 서킷브레이커 적용되어있음)

    - url : http://localhost:8085/circuitbreaker/hello

    2. 3번 이상 호출을 하면 서킷브레이커 발동되어 아래의 메세지 출력 (즉, getCircuitBreakerFallback 메소드가 실행됨을 의미)

    - waitDurationInOpenState: 10s 로 설정되어있어 10초동안 Circuit Break가 기다린다

    - minimumNumberOfCalls: 3으로 설정되어 있으므로 최소한 호출을 3번을 기록해야 실패 비율을 계산할 수 있다

    즉, 3번 이상 호출하면 서킷브레이커가 발동해서 지정한 메세지를 쏘는데 10초 지나면 서킷브레이커 상태가 OPEN → HALF_OPEN이 되서 다시 에러상태로 됨

    #04. Retry 적용

    - 실패한 실행을 짧은 지연을 가진 후 재시도

    #Controller
    
    @RestController
    @RequiredArgsConstructor
    @RequestMapping("/circuitbreaker")
    public class CircuitBreakerController {
        private final CircuitBreakerService circuitBreakerService;
     
        @GetMapping("/retryFail")
        public String retryFail() {
            return circuitBreakerService.getRetry();
        }
     
    }
    #Service
    
    @Slf4j
    @Service
    public class CircuitBreakerService {
     
        @Retry(name = Resilience4jCode.RETRY_TEST_3000, fallbackMethod = "getRetryFallback")
        public String getRetry() {
            log.info("=============== getRetry Request !!");
            runtimeException();
            return "hello world!";
        }
     
        private void runtimeException() {
            throw new RuntimeException("failed");
        }
        private String getCircuitBreakerFallback(Throwable t) {
            return "getCircuitBreakerFallback! exception type: " + t.getClass() + "exception, message: " + t.getMessage();
        }
     
    }

    [실행결과]

    1. 포스트맨으로 A서버의 api요청 (해당 서비스는 retry 적용되어있음)

    - url : http://localhost:8085/circuitbreaker/retryFail

    5초에 한번씩 재요청
    5번 재시도 후 최종적으로 retry가 발동해서 지정한 메세지 출력

    #Circuit Breaker 테스트 모니터링 + Actuator

    #01. 테스트를 위한 service 코드 수정

    #Service
    
    @Slf4j
    @Service
    public class CircuitBreakerService {
     
        @CircuitBreaker(name = Resilience4jCode.CIRCUIT_TEST_70000, fallbackMethod = "getCircuitBreakerFallback")
        public String getCircuitBreaker() {
            runtimeException();
            return "hello world!"; //익셉션나서 결국 출력 못함
        }
     
        @Retry(name = Resilience4jCode.RETRY_TEST_3000, fallbackMethod = "getRetryFallback")
        public String getRetry() {
            log.info("=============== getRetry Request !!");
            runtimeException();
            return "hello world!"; //익셉션나서 결국 출력 못함
        }
     
        private void runtimeException() {
            //as-is
            //throw new RuntimeException("failed");
     
            //to-be : 랜덤숫자출력해서 7이하일 경우에만 익셉션 발생
            int randomInt = new Random().nextInt(10);
     
            if (randomInt <= 7) {
                throw new RuntimeException("failed!!!!");
            }
        }
     
        private String getCircuitBreakerFallback(Throwable t) {
            return "getCircuitBreakerFallback! exception type: " + t.getClass() + "exception, message: " + t.getMessage();
        }
     
        private String getRetryFallback(Throwable t) {
            return "getRetryFallback! exception type: " + t.getClass() + "exception, message: " + t.getMessage();
        }
     
    }

    #02. properties 파일 수정

    ...
    #actuator 정보 노출을 위한 설정
    resilience4j.resilience4j.retry.configs.default.registerHealthIndicator=true
    ...
    #actuator 정보 노출을 위한 설정
    resilience4j.resilience4j.circuitbreaker.configs.default.register-health-indicator=true
    ...
    #actuator : spring boot를 사용하여 백엔드를 구현할 경우 애플리케이션 모니터링 및 관리 측면에 도움을 줌
    management.endpoint.health.show-details=always
    #테스트를 위해 actuator 전체 노출
    management.endpoints.web.exposure.include=*
    #circuitbreakers 정보 노출
    management.health.circuitbreakers.enabled=true
    #retryevents 정보 노출
    management.health.retryevents.enabled=true
    #전체
     
    ##서킷브레이커 적용을 위한 설정값
    #1) retry
    #최대 재시도 수
    resilience4j.retry.configs.default.max-attempts=5
    #재시도 할 때마다 기다리는 고정시간 (1초[1000ms], 기본값 : 0.5[500ms])
    resilience4j.retry.configs.default.wait-duration=5000
    #실패로 기록되는 에러 클래스 리스트. 즉, 재시도돼야 하는 에러 클래스의 리스트. empty일 경우 모든 에러 클래스를 재시도 (기본값 : empty)
    #resilience4j.retry.configs.default.retry-exceptions[0]=org.springframework.web.client.HttpServerErrorException
    #resilience4j.retry.configs.default.retry-exceptions[1]=java.io.IOException
    #무시돼야 하는 에러 클래스 리스트. 즉, 재시도 되지 않아야 할 에러 클래스 리스트. (기본값 : empty)
    #resilience4j.retry.configs.default.ignore-exceptions[0]=java.util.NoSuchElementException
    #actuator 정보 노출을 위한 설정
    resilience4j.resilience4j.retry.configs.default.registerHealthIndicator=true
     
     
    #retry name : retry-test-3000
    #기본 config 지정. ex) retry.configs.{default}
    resilience4j.retry.instances.retry-test-3000.base-config=default
    resilience4j.retry.instances.retry-test-3000.wait-duration=3000
     
    resilience4j.retry.instances.retry-db-select-4000.base-config=default
    resilience4j.retry.instances.retry-db-select-4000.wait-duration=4000
    resilience4j.retry.instances.retry-db-select-5000.base-config=default
    resilience4j.retry.instances.retry-db-select-5000.wait-duration=5000
     
    #2) circuitbreaker
    #default : 기본 config명
    resilience4j.circuitbreaker.configs.default.register-health-indicator=true
    #sliding window 타입을 결정. COUNT_BASED - slidingWindowSize만큼의 마지막 call들이 기록되고 집계 / TIME_BASED - 마지막 slidingWindowSize초 동안의 call들이 기록되고 집계
    resilience4j.circuitbreaker.configs.default.sliding-window-type=TIME_BASED
    #CLOSED 상태에서 집계되는 슬라이딩 윈도우 크기를 설정. (기본값: 100)
    resilience4j.circuitbreaker.configs.default.sliding-window-size=10
    #최소한 호출을 10번을 기록해야 실패 비율을 계산가능
    ########TODO 원복 !! resilience4j.circuitbreaker.configs.default.minimum-number-of-calls=10
    resilience4j.circuitbreaker.configs.default.minimum-number-of-calls=3
    #임계값을 백분율로 설정. 서킷브레이커는 호출에 걸리는 시간이 slowCallDurationThreshold보다 길면 느린 호출로 간주.
    #해당 값을 넘어갈 시 서킷브레이커는 OPEN상태로 전환되며, 이때부터 호출을 차단한다 (기본값: 100)
    resilience4j.circuitbreaker.configs.default.slow-call-rate-threshold=100
    #호출에 소요되는 시간이 설정한 임계치보다 길면 느린 호출로 계산. -> 응답시간이 느린것으로 판단할 기준 시간 (60초, 1000 ms = 1 sec) (기본값: 60000[ms])
    resilience4j.circuitbreaker.configs.default.slow-call-duration-threshold=60000
    #실패비율 임계치를 백분율로 설정하여 해당 값을 넘어갈 시 서킷브레이커는 OPEN 상태로 전환되며, 이때부터 호출 차단 (기본값 : 50)
    resilience4j.circuitbreaker.configs.default.failure-rate-threshold=50
    #HALF_OPEN 상태일 때, OPEN/CLOSE 여부를 판단하기 위해 허용할 호출 횟수를 설정 수 (기본값: 10)
    resilience4j.circuitbreaker.configs.default.permitted-number-of-calls-in-half-open-state=10
    #서킷의 상태가 OPEN 에서 HALF-OPEN 으로 변경되기전에 서킷브레이커가 기다리는 시간 [s] (60초, 1000 ms = 1 sec) (기본값: 60000[ms])
    resilience4j.circuitbreaker.configs.default.wait-duration-in-open-state=10s
    #actuator 정보 노출을 위한 설정
    resilience4j.resilience4j.circuitbreaker.configs.default.register-health-indicator=true
     
    #circuitbreaker name : circuit-test-70000
    #기본 config 지정. ex) circuitbreaker.configs.{default}
    resilience4j.circuitbreaker.instances.circuit-test-70000.base-config=default
    #응답시간이 느린것으로 판단할 기준 시간 [ms]
    resilience4j.circuitbreaker.instances.circuit-test-70000.slow-call-duration-threshold=70000
     
    resilience4j.circuitbreaker.instances.circuit-db-select-200.base-config=default
    resilience4j.circuitbreaker.instances.circuit-db-select-200.slow-call-duration-threshold=200
    resilience4j.circuitbreaker.instances.circuit-db-select-300.base-config=default
    resilience4j.circuitbreaker.instances.circuit-db-select-300.slow-call-duration-threshold=300
     
    #actuator : spring boot를 사용하여 백엔드를 구현할 경우 애플리케이션 모니터링 및 관리 측면에 도움을 줌
    management.endpoint.health.show-details=always
    #테스트를 위해 actuator 전체 노출
    management.endpoints.web.exposure.include=*
    #circuitbreakers 정보 노출
    management.health.circuitbreakers.enabled=true
    #retryevents 정보 노출
    management.health.retryevents.enabled=true

    #03. Actuator를 통한 모니터링

    Spring Boot에서는 Spring Boot Actuator라는 자체모니터링 툴을 제공

    - 애플리케이션의 상태를 종합적으로 정리하여 우리에게 제공 및 관리

    console 옆 actuator탭 내부에 Mappings에 여러 endpoints가 존재

    - 엔드포인트 우클릭하여 HTTP GET 요청

    http://127.0.0.1:8085/actuator/health
     
    HTTP/1.1 200
    Content-Type: application/vnd.spring-boot.actuator.v3+json;charset=UTF-8
    Transfer-Encoding: chunked
    Date: Thu, 19 Jan 2023 06:37:06 GMT
     
    {
      "groups": [],
      "status": {
        "code": "UP",
        "description": ""
      },
      "components": {
        "circuitBreakers": {
          "status": {
            "code": "UP",
            "description": ""
          },
          "details": {
            "circuit-db-select-300": {
              "status": {
                "code": "UP",
                "description": ""
              },
              "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"
              }
            },
            "circuit-db-select-200": {
              "status": {
                "code": "UP",
                "description": ""
              },
              "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"
              }
            },
            "circuit-test-70000": {
              "status": {
                "code": "UP",
                "description": ""
              },
              "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"
              }
            }
          }
        },
        "diskSpace": {
          "status": {
            "code": "UP",
            "description": ""
          },
          "details": {
            "total": 494384795648,
            "free": 406765613056,
            "threshold": 10485760
          }
        },
        "ping": {
          "status": {
            "code": "UP",
            "description": ""
          },
          "details": {}
        }
      }
    }
    Response file saved.
    > 2023-01-19T153707.200.json
     
    Response code: 200; Time: 415ms (415 ms); Content length: 1209 bytes (1.21 kB)

    #04. @Circuitbreaker 관련 api 호출할때 모니터링

    - circuitbreaker가 실행되지 않을때 (초기상태)

    http://127.0.0.1:8085/actuator/circuitbreakerevents
     
    HTTP/1.1 200
    Content-Type: application/vnd.spring-boot.actuator.v3+json;charset=UTF-8
    Transfer-Encoding: chunked
    Date: Thu, 19 Jan 2023 06:56:12 GMT
     
    {
      "circuitBreakerEvents": []
    }
    Response file saved.
    > 2023-01-19T155612.200.json
     
    Response code: 200; Time: 162ms (162 ms); Content length: 27 bytes (27 B)

    - 설정해놓은 임계치를 넘은 실패에 의해서 @Circuitbreaker 실행되어 Circuit이 OPEN 되었을 때

    http://127.0.0.1:8085/actuator/circuitbreakerevents
     
    HTTP/1.1 200
    Content-Type: application/vnd.spring-boot.actuator.v3+json;charset=UTF-8
    Transfer-Encoding: chunked
    Date: Thu, 19 Jan 2023 06:58:58 GMT
     
    {
      "circuitBreakerEvents": [
        {
          "circuitBreakerName": "circuit-test-70000",
          "type": "ERROR",
          "creationTime": "2023-01-19T15:58:40.530+09:00[Asia/Seoul]",
          "errorMessage": "java.lang.RuntimeException: failed!!!!",
          "durationInMs": 57
        },
        {
          "circuitBreakerName": "circuit-test-70000",
          "type": "ERROR",
          "creationTime": "2023-01-19T15:58:41.830+09:00[Asia/Seoul]",
          "errorMessage": "java.lang.RuntimeException: failed!!!!",
          "durationInMs": 0
        },
        {
          "circuitBreakerName": "circuit-test-70000",
          "type": "ERROR",
          "creationTime": "2023-01-19T15:58:42.713+09:00[Asia/Seoul]",
          "errorMessage": "java.lang.RuntimeException: failed!!!!",
          "durationInMs": 0
        },
    //3번 이상 호출해서 설정한 임계치 넘음
        {
          "circuitBreakerName": "circuit-test-70000",
          "type": "FAILURE_RATE_EXCEEDED",
          "creationTime": "2023-01-19T15:58:42.714+09:00[Asia/Seoul]"
        },
    //circuit이 CLOSE -> OPEN으로 변경
        {
          "circuitBreakerName": "circuit-test-70000",
          "type": "STATE_TRANSITION",
          "creationTime": "2023-01-19T15:58:42.718+09:00[Asia/Seoul]",
          "stateTransition": "CLOSED_TO_OPEN"
        },
    //이후의 호출은 모두 차단
        {
          "circuitBreakerName": "circuit-test-70000",
          "type": "NOT_PERMITTED",
          "creationTime": "2023-01-19T15:58:43.521+09:00[Asia/Seoul]"
        },
        {
          "circuitBreakerName": "circuit-test-70000",
          "type": "NOT_PERMITTED",
          "creationTime": "2023-01-19T15:58:44.648+09:00[Asia/Seoul]"
        }
      ]
    }
    Response file saved.
    > 2023-01-19T155858.200.json
     
    Response code: 200; Time: 126ms (126 ms); Content length: 1150 bytes (1.15 kB)

    - Circuit이 HALF_OPEN 으로 되었을 때

    {
          "circuitBreakerName": "circuit-test-70000",
          "type": "NOT_PERMITTED",
          "creationTime": "2023-01-19T16:22:08.668+09:00[Asia/Seoul]"
        },
    //10초 이후 OPEN -> HALF_OPEN으로 상태변경
        {
          "circuitBreakerName": "circuit-test-70000",
          "type": "STATE_TRANSITION",
          "creationTime": "2023-01-19T16:22:11.071+09:00[Asia/Seoul]",
          "stateTransition": "OPEN_TO_HALF_OPEN"
        },
    //랜덤한 숫자에 의해서 성공
        {
          "circuitBreakerName": "circuit-test-70000",
          "type": "SUCCESS",
          "creationTime": "2023-01-19T16:22:11.072+09:00[Asia/Seoul]",
          "durationInMs": 0
        },
        {
          "circuitBreakerName": "circuit-test-70000",
          "type": "ERROR",
          "creationTime": "2023-01-19T16:22:13.098+09:00[Asia/Seoul]",
          "errorMessage": "java.lang.RuntimeException: failed!!!!",
          "durationInMs": 0
        },
        {
          "circuitBreakerName": "circuit-test-70000",
          "type": "ERROR",
          "creationTime": "2023-01-19T16:22:15.094+09:00[Asia/Seoul]",
          "errorMessage": "java.lang.RuntimeException: failed!!!!",
          "durationInMs": 0
        },
    //다시 상태 HALF_OPEN -> OPEN으로 상태변경
        {
          "circuitBreakerName": "circuit-test-70000",
          "type": "STATE_TRANSITION",
          "creationTime": "2023-01-19T16:22:15.096+09:00[Asia/Seoul]",
          "stateTransition": "HALF_OPEN_TO_OPEN"
        },
    //이후 모든 호출 차단
        {
          "circuitBreakerName": "circuit-test-70000",
          "type": "NOT_PERMITTED",
          "creationTime": "2023-01-19T16:22:16.994+09:00[Asia/Seoul]"
        }

    - circuit이 CLOSED로 되었을 때

     

     

    #05. @Retry 관련 api 호출할때 모니터링

    - Retry가 실행되지 않을때 (초기상태)

    http://127.0.0.1:8085/actuator/retryevents
     
    HTTP/1.1 200
    Content-Type: application/vnd.spring-boot.actuator.v3+json;charset=UTF-8
    Transfer-Encoding: chunked
    Date: Thu, 19 Jan 2023 07:36:13 GMT
     
    {
      "retryEvents": []
    }
    Response file saved.
    > 2023-01-19T163613.200.json
     
    Response code: 200; Time: 265ms (265 ms); Content length: 18 bytes (18 B)

    - 설정해놓은 임계치를 넘은 재시도에 의해서 @Retry 실행 되었을 때 (type: ERROR 인 객체가 표시된다.)

    http://127.0.0.1:8085/actuator/retryevents
     
    HTTP/1.1 200
    Content-Type: application/vnd.spring-boot.actuator.v3+json;charset=UTF-8
    Transfer-Encoding: chunked
    Date: Thu, 19 Jan 2023 07:45:17 GMT
     
    {
      "retryEvents": [
        {
          "retryName": "retry-test-3000",
          "type": "RETRY",
          "creationTime": "2023-01-19T16:44:48.953+09:00[Asia/Seoul]",
          "errorMessage": "java.lang.RuntimeException: failed!!!!",
          "numberOfAttempts": 1
        },
        {
          "retryName": "retry-test-3000",
          "type": "RETRY",
          "creationTime": "2023-01-19T16:44:51.958+09:00[Asia/Seoul]",
          "errorMessage": "java.lang.RuntimeException: failed!!!!",
          "numberOfAttempts": 2
        },
        {
          "retryName": "retry-test-3000",
          "type": "RETRY",
          "creationTime": "2023-01-19T16:44:54.970+09:00[Asia/Seoul]",
          "errorMessage": "java.lang.RuntimeException: failed!!!!",
          "numberOfAttempts": 3
        },
        {
          "retryName": "retry-test-3000",
          "type": "RETRY",
          "creationTime": "2023-01-19T16:44:57.977+09:00[Asia/Seoul]",
          "errorMessage": "java.lang.RuntimeException: failed!!!!",
          "numberOfAttempts": 4
        },
        {
          "retryName": "retry-test-3000",
          "type": "ERROR",
          "creationTime": "2023-01-19T16:45:00.989+09:00[Asia/Seoul]",
          "errorMessage": "java.lang.RuntimeException: failed!!!!",
          "numberOfAttempts": 5
        }
      ]
    }
    Response file saved.
    > 2023-01-19T164517.200.json
     
    Response code: 200; Time: 123ms (123 ms); Content length: 2585 bytes (2.58 kB)

    - 설정해놓은 임계치 이전에 성공을 하였을때 (type: SUCCESS 인 객체가 표시된다. 성공하였으므로 numberOfAttempts는 증가하지 않음)

    http://127.0.0.1:8085/actuator/retryevents
     
    HTTP/1.1 200
    Content-Type: application/vnd.spring-boot.actuator.v3+json;charset=UTF-8
    Transfer-Encoding: chunked
    Date: Thu, 19 Jan 2023 07:39:37 GMT
     
    {
      "retryEvents": [
        {
          "retryName": "retry-test-3000",
          "type": "RETRY",
          "creationTime": "2023-01-19T16:39:08.309+09:00[Asia/Seoul]",
          "errorMessage": "java.lang.RuntimeException: failed!!!!",
          "numberOfAttempts": 1
        },
        {
          "retryName": "retry-test-3000",
          "type": "SUCCESS",
          "creationTime": "2023-01-19T16:39:11.343+09:00[Asia/Seoul]",
          "errorMessage": "java.lang.RuntimeException: failed!!!!",
          "numberOfAttempts": 1
        }
      ]
    }
    Response file saved.
    > 2023-01-19T163937.200.json
     
    Response code: 200; Time: 45ms (45 ms); Content length: 385 bytes (385 B)

     


    #참고링크

    - resilience4j란 (https://sabarada.tistory.com/204)

    - 서킷브레이커(=Circuitbreaker) Resilience4j 적용 (Java + Spring Boot) (https://bkjeon1614.tistory.com/711)

    - Spring Boot Actuator, Application을 모니터링 하자 (https://sabarada.tistory.com/23)

    '개발공부 > 서버 네트워크' 카테고리의 다른 글

    특정 단어 로그 건수 파악 방벚  (0) 2024.03.22
    Circuit Breaker #01. 개념  (0) 2023.01.13
Designed by Tistory.