서킷 브레이커 사용하는 이유 : 마이크로서비스의 오류를 줄이고 탄력성을 향상
❓
IN) 마이크로서비스 사이의 통신이 이루어지는 대규모 시스템 환경에서
HOW) 동기 방식으로
WHAT) 느리거나 응답하지 않는 downstream 마이크로서비스로 인한 피해를 최소화
retry(재시도)와 서킷 브레이커 메커니즘 : 동기 방식으로 연결되는 소프트웨어 컴포넌트 (LIKE 마이크로서비스)에서 특히 유용, 에지 서버를 제외한 모든 마이크로서비스에서 Resilience4j를 사용 可.
- 에지 서버에선 Resilience4j 사용 不可
b/c ) 스프링 클라우드 게이트웨이가 예전 서킷 브레이커인 Netflix Hystrix만 지원 하기 때문
이번장은 그림처럼 product-composite 서비스에서 product 서비스를 호출하는 부분에 서킷 브레이커 및 재시도 메커니즘을 적용
상태 다이어그램
SO) 마이크로서비스는 장애에 대한 탄력성가짐 == 이는 동기 방식으로 통신하는 마이크로서비
스 시스템 환경의 필수 기능
런타임시 서킷 브레이커의 정보를 공개 by Resilience4j
1 액추에이터의 상태 점검 엔드포인트(actuator/health): 서킷 브레이커의 현재 상태 모니터링
2 액추에이터 엔드포인트(/actuator/circuitbreakerevents): 서킷 브레이커의 상태 전이 등의 이벤트 게시됨
3 프로메테우스와 같은 모니터링 도구에 메트릭을 게시 (서킷 브레이커는 스프링 부트의 메트릭 시스템과 통합돼 있기때문)
서킷브레이커 로직 제어하는 Resilience4J를 구성 매개변수
ringBufferSizeInClosedState: 닫힌 상태에서의 호출 수로, 서킷을 열어야 할지
결정할 때 사용한다.
failureRateThreshold: 실패한 호출에 대한 임계값(백분율)으로 이 값을 초과하
면 서킷이 열린다.
.. 등이 있음 실습하면서 살펴보겠음
ringBufferSizeInClosedState 5, failureRateThreshold = 50%
(== 마지막 5개의 호출 중 3개 이상이 실패하면 서킷열림 + 2개 이상의 호출이 실패하면 서킷이 다시 열리며, 이 외의 경우엔 서킷이 닫힌다.)
waitInterval = 10000, automaticTransitionFromOpenToHalfOpenEnabled = true
(서킷 브레이커는 10초 동안 서킷을 열린 상태로 유지했다가 반열림 상태로 전환)
ringBufferSizeInHalfOpenState =3
(서킷 브레이커는 서킷이 반열림 상태로 전 환된 후에 처음 유입된 3개의 호출을 기준 삼아 서킷의 열림 여부를 결정)
ignoreExceptions InvalidInputException 및 NotFoundException
(이 비즈니스 예외는 서킷 브레이커에서 오류로 여기지 않음)
retry 메커니즘
: 설정된 대기 시간을 사이에 두고, 실패한 요청을 여러 번 다시 시도하는 것
++ 일시적인 네트워크 결함과 같이 무작위로 드물게 발생하는 오류에 매우 유용.
사용 조건)
재시도 대상 서비스에 멱등성이 있어야 한다
(==같은 요청 매개 변수로 서비스를 여러 번 호출하더라도 결과는 항상 같아야 함)
Resilience4j는 서킷 브레이커와 같은 방식으로 재시도와 관련된 이벤트 및 메트릭 정보
를 공개
BUT)상태 정보는 전혀 제공X
재시도 이벤트에 관한 정보는 액추에이터 엔드포인트(Jactuator/retryevents)에서 얻을 수 있음
임의로 오류를 발생시키고 지연을 삽입하기 위한 코드를 먼저 추가
-> 응답이 늦거나 전혀 응답하지않는 API를 처리하기 위한 서킷 브레이커를 추가 -> 무작위로 발생하는 결함을 처리하 고자 재시도 메커니즘을 추가
1) delay와 2) faultPercentage 쿼리 매개변수 추가
1) : product 마이크로서비스에 있는 getProduct API의 응답을 지연시킴
2) : getProduct API가 쿼리 매개변수로 지정한 백분율(0~100%)에 따라 무작위로 예외를 발생
를 productCompositeService와 productService에 추가
빨간색 나는건 의존성없어서 그런검므로
Resilience4j에 대한 스타터 의존성을 빌드 파일에 추가
product에 무작위 오류 생성 + 지연 삽입
지연함수 - sleep
오류 생성 함수 - 무작위 : 1에서 100 사이의 수가 지정된 오류 백분율보다 크거나 같으면 예외를 발생
서킷 브레이커 및 시간 초과 로직 추가
@CircuitBreaker : 서킷 브레이커가 적용
서킷 브레이커는 시간 초과로 트리거X 예외가 발생해야 트리거O.
WHEN) 시간 초과가 발생하면-> 예외를 생성하는 코드를 추가해 서킷 브레이커를 트리거.
폴백 로직을 적용하려면 서킷이 열려 있을 때 발생하는 예외인 CircuitBreakerOpenException을 잡아야함
예외를 잡는 getProductFallbackValue
13이면 NotFoundException을 던지고, 이외의 경우엔 하드 코딩된 값을 반환
앞에서 구성 추가한다고 했던거 추가해주기 product-composite.yml
어노테이션은 아까 서킷 브레이커하면서 붙였고 예외처리 해주고 구성추가
@Retry 메서드에서 던진 예외--> 재시도 메커니즘에 의해 RetryExceptionWrapper 예외로 wrapping.
메서드호출자가 RetryExceptionwrapper 예외를 벗겨 내고 실제 예외로 대체하는 로직을 추가 (IF 메서드에서 던진 예외를 직접 확인해야 하는 상황)( ex. CircuitBreakerOpenException을 잡아서 폴백 메소드를 적용할 때처럼)
빌드 헀더니 test 코드 쪽에서 에러남
ProductCompositeServiceApplicationTests 에 이 코드 넣어주어야함
그렇지만 여전히 test 쪽에서 에러남
product-composite 마이크로서비스에 추가한 product 서킷 브레이커의 상태를 확인하려면
docker run --rm -it --network=my-network alpine wget product-composite:8080/actuator/ health -90 - | jq -r .details.productCircuitBreaker.details.state
서킷 브레이커 닫혀있는 상태에서
for((n=0; n<3; n++))
do
assertCurl 500 "curl -k https://$HOST:$PORT/product-composite/$PROD_ID_REVS_
RECS?delay=3 $AUTH -s"
message=$(echo $RESPONSE jq -r.message)
assertEqual "Did not observe any item or terminal signal within 2000ms'
"${message:0:57}"
done
이용해서 세번 반복하면 --> 서킷브레이커 열림 ==> product 서비스의 늦은응답 때문에 커맨드는 모두 실패
서킷이 열려 있으면 "빠른 실패 로직이 실행되므로" 시간 초과를 기다리지 않고 바로 응답을
반환하고 폴백 메서드를 호출해 최적화된 응답을 생성가능
assertCurl 404 "curl -k https://$HOST:$PORT/product-composite/$PROD_ID_NOT_FOUND $AUTH -s" assertEqual "Product Id: $PROD_ID_NOT_FOUND not found in fallback cache!" "$(echo $RESPONSE | jq -r .message)"
구성한 대로 서킷 브레이커는 10초가 지나면 반열림 상태로 전환 --> sleep 10 던지고 -> 정상 요청을 보내는 테스트를 세 번 실행 ==> 닫힘 상태인지 확인
assertEqual "HALF_OPEN" "$($EXEC wget product-composite:8080/actuator/health -go - jq
-r.details.productcircuitBreaker.details.state)"
for ((n=0; n<3; n++))
do
assertCurl 200 "curl -k https://$HOST:$PORT/product-composite/$PROD_ID_REVS_RECS
$AUTH -S"
assertEqual "product name C" "$(echo "$RESPONSE" | jq -r .name)"
done
assertEqual "CLOSED" "$($EXEC wget product-composite:8080/actuator/health -90 - | jq -r
.details.productCircuitBreaker.details.state)"
13장에서는 Resilience4j를 사용해봣다.
build도 안되고 bash 테스트 스크립트로도 테스트를 못하니 좀 답답했다
Resilience4j, 서킷 브레이커, 재시도 메커니즘의 작동 방식을 살펴봤는데, 서킷 브레이커는 서킷이 열려 있을 때 빠른 실패, 폴백 메서드를 작동시키고 이를 이용해 동기 서비스가 정상적으로 응답하지 않을 때도 서비스가 응답 불능 상태에 빠지지는 것을 미연에 방지한다. 서킷이 반열림 상태에 있을 때는 장애가 발생한 서비스가 정상 상태로 돌아왔는지 확인하고, 다시 요청을 처리할 수 있도록 서킷을 닫아서 마이크로서비스의 탄력성을 높이는 기능을 가지고 있다.
재시도 메커니즘은 가끔 발생하는 문제(LIKE 일시적인 네트워크 결함) 으로 전송하지 못한 요청을 재전송 시도 ==> 멱등성 있는 서비스에만 적용 가능
MSA2 스터디가 한장을 앞두고 있다는 것이 놀랍다
이론상으로라도 서비스의 탄력성을 가지게 하는 얘를 알게돼서 좋다...
여전히 프로젝트 적용에는 아직 무리인 것 같은 생각은 동일하다
나머지 마지막 한 장도 파이팅..