-
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 권고
- Spring Boot 2.4.X 부터는 더 이상 지원하지 않음 [Link]
- Resilience4j
- Resilience4j 는 Netflix Hystrix 로 부터 영감을 받은 Fault Tolerance Libray
- 사용하기 가볍고 다른 라이브러리 의존성이 없음
- Java 전용으로 개발된 경량화된 Fault Tolerance Libray
- Spring Boot 전용은 아님
- 대표적인 패턴
- Retry
- 실패한 실행을 짧은 지연을 가진 후 재시도합니다.
- Circuit Breaker
- 실패한 실행에 대해서 또 다른 시도가 들어올 때 바로 실패 처리합니다.
- Retry
#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 - Netflix Hystrix