- 서킷 브레이커
- Resilience4j
- API 게이트웨이
- Spring Cloud Gateway
서킷 브레이커는 마이크로서비스 간의 호출 실패를 감지하고 시스템의 전체적인 안정성을 유지하는 패턴으로, 서비스 호출 실패 시 빠른 실패를 통해 장애가 난 부분을 격리하고, 전체 시스템에는 영향을 주지 않도록 한다.
Resilience4j는 서킷 브레이커를 수행하는 라이브러리이다.
Resilience4는 Closed, Open, Half-Open 상태를 통해 호출 실패를 관리한다.
- Closed
- 가장 기본적인 상태로, 모든 요청을 통과시킨다.
- Closed 상태에서 호출이 실패하면 실패 카운터를 증가시킨다.
- 실패율이 미리 설정된 임계값을 초과하면 상태를 Open으로 전환한다.
- Open
- Open 상태에서는 모든 요청을 즉시 실패로 처리하고, 에러 응답을 반환한다.
- 미리 설정된 대기 시간이 지나면 상태를 Half-Open으로 전환한다.
- Half-Open
- Half-Open 상태에서는 제한된 수의 요청을 허용하여 시스템이 정상으로 복구되었는지 확인한다.
- 요청이 모두 성공하면 상태를 Closed로 전환한다.
- 요청이 하나라도 실패하면 상태를 Open으로 전환한다.
또한 Resilience4j는 호출 실패 시 대체 로직을 제공하는 Fallback, 서킷 브레이커 상태를 모니터링하고 관리할 수 있는 다양한 도구를 제공한다.
Resilience4j 설정은 application.yml 파일에서 다음과 같이 설정할 수 있다.
resilience4j:
circuitbreaker:
configs:
default: # 기본 구성 이름
registerHealthIndicator: true # 애플리케이션의 헬스 체크에 서킷 브레이커 상태를 추가하여 모니터링 가능
# 서킷 브레이커가 동작할 때 사용할 슬라이딩 윈도우의 타입을 설정
# COUNT_BASED: 마지막 N번의 호출 결과를 기반으로 상태를 결정
# TIME_BASED: 마지막 N초 동안의 호출 결과를 기반으로 상태를 결정
slidingWindowType: COUNT_BASED # 슬라이딩 윈도우의 타입을 호출 수 기반(COUNT_BASED)으로 설정
# 슬라이딩 윈도우의 크기를 설정
# COUNT_BASED일 경우: 최근 N번의 호출을 저장
# TIME_BASED일 경우: 최근 N초 동안의 호출을 저장
slidingWindowSize: 5 # 슬라이딩 윈도우의 크기를 5번의 호출로 설정
minimumNumberOfCalls: 5 # 서킷 브레이커가 동작하기 위해 필요한 최소한의 호출 수를 5로 설정
slowCallRateThreshold: 100 # 느린 호출의 비율이 이 임계값(100%)을 초과하면 서킷 브레이커가 동작
slowCallDurationThreshold: 60000 # 느린 호출의 기준 시간(밀리초)으로, 60초 이상 걸리면 느린 호출로 간주
failureRateThreshold: 50 # 실패율이 이 임계값(50%)을 초과하면 서킷 브레이커가 동작
permittedNumberOfCallsInHalfOpenState: 3 # 서킷 브레이커가 Half-open 상태에서 허용하는 최대 호출 수를 3으로 설정
# 서킷 브레이커가 Open 상태에서 Half-open 상태로 전환되기 전에 기다리는 시간
waitDurationInOpenState: 20s # Open 상태에서 Half-open 상태로 전환되기 전에 대기하는 시간을 20초로 설정
Fallback 메서드는 외부 서비스 호출이 실패했을 때 대체 로직을 제공하는 메서드이다. 다음과 같이 작성할 수 있다.
@Service
public class MyService {
@CircuitBreaker(name = "myService", fallbackMethod = "fallbackMethod")
public String myMethod() {
// 외부 서비스 호출
return externalService.call();
}
public String fallbackMethod(Throwable t) {
return "Fallback response";
}
}
Fallback은 장애가 발생해도 사용자에게 일정한 응답을 제공할 수 있도록 하고, 특정 서비스의 장애가 전체 서비스에 영향을 주지 않도록 장애의 전파를 방지한다.
Resilience4j Dashboard를 통해 서킷 브레이커 상태(Closed, Open, Half-Open)를 모니터링 할 수 있다.
Prometheus와 Grafana를 통해 실시간 모니터링이 가능하며, Prometheus를 통해 수집된 메트릭을 Grafana 대시보드에서 시각화한다.
Resilience4j는 Spring Cloud Netflix 패키지에 포함되어 있어 다른 요소들과 쉽게 통합 사용이 가능하다.

서킷 브레이커 상태 변화와 Fallback 메서드가 호출되는 것을 확인할 수 있다.
API Gateway는 클라이언트의 요청을 백엔드 서비스로 라우팅하고 다양한 부가 기능을 제공하는 중간 서버이다.
클라이언트는 API Gateway를 통해서만 서비스에 진입할 수 있다.
API Gateway는 로깅, 모니터링, 요청 필터링 등의 부가 기능들도 처리할 수 있다.
API 게이트웨이의 주요 기능
- 라우팅 : 클라이언트의 요청을 적절한 서비스로 전달
- 인증 및 권한 부여 : 요청의 인증 및 권한을 API Gateway에서 미리 검증해 서비스로 전달
- 로드 밸런싱 : 같은 서비스의 여러 인스턴스 간의 부하 분산
- 모니터링 및 로깅 : 요청 및 응답을 로깅 및 모니터링
- 요청 및 응답 변환 : 요청과 응답을 변환하거나 필터링해 서비스 또는 클라이언트로 전달
Spring Cloud Gateway는 Spring Cloud Netflix 패키지에서 제공하는 API 게이트웨이 라이브러리로, MSA에서 널리 사용되며, Eureka와 쉽게 통합이 가능하다.
Spring Cloud Gateway의 특징
- 동적 라우팅 : 요청의 URL 패턴에 따라 동적으로 라우팅
- 필터링 : 요청 전후에 다양한 작업을 수행할 수 있는 필터 체인 제공
- 모니터링 : 요청 로그 및 메트릭을 통해 서비스 상태 모니터링
- 보안 : 요청의 인증 및 권한 검증
application.yml 파일을 통해 라우팅 설정이 가능하다.
spring:
cloud:
gateway:
discovery:
locator:
enabled: true # 서비스 디스커버리를 통해 동적으로 라우트를 생성하도록 설정
routes:
- id: users-service # 라우트 식별자
uri: lb://users-service # 'users-service'라는 이름으로 로드 밸런싱된 서비스로 라우팅
predicates:
- Path=/users/** # /users/** 경로로 들어오는 요청을 이 라우트로 처리
- id: orders-service # 라우트 식별자
uri: lb://orders-service # 'orders-service'라는 이름으로 로드 밸런싱된 서비스로 라우팅
predicates:
- Path=/orders/** #/orders/** 경로로 들어오는 요청을 이 라우트로 처리
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
Spring Cloud Gateway에서 제공하는 필터는 모든 요청에 대해 작동하는 Global Filter와 특정 라우트에만 적용되는 Gateway Filter가 있다.
필터를 구현하기 위해서는 GlobalFilter 또는 GatewayFilter 인터페이스를 구현하고, filter 메서드를 오버라이드한다.
필터 주요 객체
- Mono
Mono는 0 또는 1개의 데이터를 비동기적으로 처리Mono<Void>는 아무 데이터도 반환하지 않음을 의미- ServerWebExchange
ServerWebExchange는 HTTP 요청과 응답을 캡슐화한 객체exchange.getRequest()로 HTTP 요청을 가져옴exchange.getResponse()로 HTTP 응답을 가져옴- GatewayFilterChain
GatewayFilterChain은 여러 필터를 체인처럼 연결chain.filter(exchange)는 다음 필터로 요청을 전달
Pre 필터는 요청이 처리되기 전에 요청을 가로채고 필요한 작업을 수행한 후, 체인의 다음 필터로 요청을 전달한다.
@Component
public class PreFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 요청 로깅
System.out.println("Request: " + exchange.getRequest().getPath());
return chain.filter(exchange);
}
@Override
public int getOrder() { // 필터의 순서를 지정합니다.
return -1; // 필터 순서를 가장 높은 우선 순위로 설정합니다.
}
}
Post 필터는 요청이 처리되고 응답이 반환되기 전에 실행된다. Post 필터에서는 체인의 다음 필터가 완료된 후에 실행되어야 하는 추가적인 작업을 수행하게 되므로, chain.filter(exchange)를 호출해 다음 필터를 수행하고, 이 작업은 비동기이므로 then 메서드를 통해 응답이 완료된 후 실행할 콜백 메서드를 등록한다.
@Component
public class PostFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
// 응답 로깅
System.out.println("Response Status: " + exchange.getResponse().getStatusCode());
}));
}
@Override
public int getOrder() {
return -1;
}
}