반응형
MSA 환경에서는 작은 규모의 서비스가 상호작용하며 서로 연결되어 있다. 이런 환경에서 하나의 서비스가 장애가 나 서비스가 중단되면 다른 서비스들로 그 장애가 전파될 수 있다. 장애 전파를 막을 수 있는 여러 가지 방법들이 존재하는데, 그 중 하나는 "서킷 브레이커" 이다.
"서킷 브레이커" 라는 용어는 원래 전기 회로에서 과열된 회로를 차단하는 장치를 의미하는데, 보통 주식에서 많이 접한다. 주가가 특정 % 이상 급락하는 경우 발동하며 매매를 일시 정지할 수 있는 제도이다. 마찬가지로 MSA 환경에서의 서킷 브레이커는 발생한 장애를 다른 서비스로 전파하지 않도록 하기 위한 장치라고 보면 된다.
서킷 브레이커 종류
- Netflix Hystrix. 넷플릭스에서 만든 서킷 브레이커로 SpringBoot 2.4 버전 부터는 지원이 안된다.
- Resilience4j. 넷플릭스 서킷 브레이커가 지원 종료되면서 많이 사용하는 라이브러리
서킷 브레이커 상태
크게 3가지 상태가 존재한다. OPEN (열림), HALF-OPEN (반-열림), CLOSED (닫힘)
CLOSED
- 초기 상태
- 모든 요청을 그대로 허용
OPEN
- 에러율이 특정 임계치를 넘어서면 CLOSED 에서 OPEN 으로 변경
- 모든 요청은 차단
- 다운 스트림으로의 요청은 보내지 않고, 바로 에러를 발생시킴
HALF-OPEN
- OPEN 상테에서 일정 시간이 지나면 HALF-OPEN 상태로 변경
- 요청을 다시 다운 스트림으로 보내 성공하는지 확인
- 특정 임계치만큼 성공하면 다시 CLOSED 상태로 변경, 실패하면 OPEN 상태로 변경
의존성
- resilience4j 에서 제공하는 서킷 브레이커를 사용한다.
- @CircuitBreaker 어노테이션을 부여하여 스프링 AOP 기능을 사용해야 하기 때문에 aop 의존성도 추가해준다.
implementation("io.github.resilience4j:resilience4j-spring-boot2")
implementation("org.springframework.boot:spring-boot-starter-aop")
설정 (application.yml)
- 설정은 application.yml 파일에 적용해두었다.
- 기본적으로 아래 기본 property 만 적용했고, 추가로 필요한 property 는 여기를 참고하면 된다.
resilience4j.circuitbreaker:
configs:
default:
failureRateThreshold: 50 # 실패한 호출에 대해 서킷이 열리게 되는 임계값 (백분율)
slowCallRateThreshold: 100 # 느린 호출에 대해 서킷이 열리게 되는 임계값 (백분율)
slowCallDurationThreshold: 5s # 느린 호출로 판단하기 위한 기준시간
slidingWindowType: COUNT_BASED # 서킷이 `CLOSED` 상태에서 호출결과를 기록할때 사용하는 슬라이딩 윈도우 타입. 카운트 또는 시간기반으로 동작한다.
slidingWindowSize: 10 # 카운트기반으로 동작할때는 카운트 수, 시간기반으로 동작할때는 초
waitDurationInOpenState: 5s # 서킷이 `OPEN` 상태에서 `HALF_OPEN` 상태로 전환하기까지 시간
instances:
BlogHttpClient:
registerHealthIndicator: true
baseConfig: default
코드
- 블로그 문서를 조회하는 searchBlogDocuments 메소드에 @CircuitBreaker 어노테이션을 부여한다
- 만약 기본으로 호출하는 callKakao 메소드가 실패하면 fallbackMethod 로 기입되어 있는 callNaver 메소드가 실행된다.
@Slf4j
@Component
public class BlogHttpClient implements BlogPort {
private final KakaoBlogHttpClient kakaoBlogHttpClient;
private final NaverBlogHttpClient naverBlogHttpClient;
public BlogHttpClient(KakaoBlogHttpClient kakaoBlogHttpClient, NaverBlogHttpClient naverBlogHttpClient) {
this.kakaoBlogHttpClient = kakaoBlogHttpClient;
this.naverBlogHttpClient = naverBlogHttpClient;
}
@Override
@CircuitBreaker(name = "BlogHttpClient", fallbackMethod = "callNaver")
public Blog searchBlogDocuments(BlogSearchClause clause) {
return callKakao(clause);
}
public Blog callKakao(BlogSearchClause clause) {
return kakaoBlogHttpClient.searchBlogDocuments(KakaoBlogSearchClause.of(clause));
}
public Blog callNaver(BlogSearchClause clause, Throwable e) {
log.warn("카카오 블로그 조회에 실패하여 네이버 블로그 조회로 변경합니다. errMsg: {}", e.getMessage(), e);
return naverBlogHttpClient.searchBlogDocuments(NaverBlogSearchClause.of(clause));
}
}
반응형
'스프링' 카테고리의 다른 글
[Gradle] Kotlin DSL 과 buildSrc 를 통한 버전 관리 (0) | 2023.02.20 |
---|---|
[Gradle] Build Lifecycle (0) | 2023.02.16 |
[스프링] Executable jar 파일 만들어서 실행해보기 (0) | 2023.02.10 |
[스프링] H2 데이터베이스 사용하기 (0) | 2023.02.09 |
[스프링] 멀티 모듈 환경에서 application.yml 사용하기 (0) | 2023.02.09 |
댓글