서킷 브레이커
서킷 브레이커는 마이크로서비스 간의 호출 실패를 감지하고 시스템의 전체적인 안정성을 유지하는 패턴
외부 서비스 호출 실패 시 빠른 실패를 통해 장애를 격리하고, 시스템의 다른 부분에 영향을 주지 않도록 함.
상태 변화 : 클로즈드 → 오픈 → 하프-오픈
Resilience4j
Resilience4j : 서킷 브레이커 라이브러리로, 서비스 간의 호출 실패를 감지하고 시스템의 안정성을 유지한다.
서킷 브레이커 상태
- 클로즈드 : 기본 상태로, 모든 요청을 통과 시킴. 이 상태에서 호출이 실패하면 실패 카운터가 증가함. 실패율이 설정된 임계값을 초과하면 서킷 브레이커가 오픈 상태로 전환됨.
- 오픈 : 서킷 브레이커가 오픈 상태로 전환되면 모든 요청을 즉시 실패로 처리, 에러 응답 반환. 설정된 대기 시간이 지난 후 서킷 브레이커는 하프-오픈 상태로 전환 됨.
- 하프-오픈 : 하프-오픈 상태에서는 제한된 수의 요청을 허용하여 시스템이 정상 상태로 복구되었는지 확인 함. 요청이 성공하면 서킷 브레이커는 클로즈드 상태로 전환.
API 게이트웨이 Spring Cloud Gateway
API 게이트웨이 : 클라이언트의 요청을 받아 백엔드 서비스로 라우팅하고, 다양한 부가 기능을 제공하는 중간 서비스.
클라이언트와 서비스 간의 단일 진입점 역할을 하며 보안, 로깅, 모니터링, 요청 필터링 등을 처리 한다.
주요 기능
- 라우팅 : 클라이언트 요청을 적절한 서비스로 전달
- 인증 및 권한 부여 : 요청의 인증 및 권한 검증
- 로드 밸런싱 : 여러 서비스 인스턴스 간의 부하 분산
- 모니터링 및 로깅 : 요청 및 응답을 로깅하고 모니터링
- 요청 및 응답 변환 : 요청과 응답을 변환하거나 필터링
Spring Cloud Gateway
Spring Cloud Gateway는 Spring 프로젝트의 일환으로 개발된 API 게이트웨이로, 클라이언트 요청을 적절한 서비스로 라우팅하고 다양한 필터링 기능을 제공한다.
주요 기능
- 동적 라우팅 : 요청의 URL 패턴에 따라 동적으로 라우팅
- 필터링 : 요청 전후에 다양한 작업을 수행할 수 있는 필터 체인 제공
- 모니터링 : 요청 로그 및 메트릭을 통해 서비스 상태 모니터링
- 보안 : 요청의 인증 및 권한 검증
Spring Cloud Gateway 필터링
- Global Filter : 모든 요청에 대해 작동하는 필터
- Gateway Filter : 특정 라우트에만 적용되는 필터
필터 구현 → GlobalFilter 또는 GatewayFilter 인터페이스를 구현하고, filter 메소드를 오버라이드 해야 함.
필터 주요 객체
- Mono : 리액티브 프로그래밍에서 0 또는 1개의 데이터를 비동기적으로 처리. Mono<Void>는 아무 데이터도 반환하지 않음을 의미
- ServerWebExchange : HTTP 요청과 응답을 캡슐화한 객체. exchange.getRequest( )로 요청을 가져오고, exchange.getResponse( ) 로 응답을 가져옴
- 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; // 필터 순서를 가장 높은 우선 순위로 설정합니다.
}
}
Pre 필터는 요청이 처리되기 전에 실행됨. 따라서 Pre 필터에서는 요청을 가로채고 필요한 작업을 수행한 다음, 체인의 다음 필터로 요청을 전달. 이 때 추가적인 비동기 작업을 수행할 필요가 없기 때문에 then 메소드 사용할 필요 없음.
Post 필터
@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;
}
}
Post 필터는 요청이 처리 된 후, 응답이 반환되기 전에 실행 됨. Post 필터에서는 체인의 다음 필터가 완료된 후에 실행되어야 하는 추가적인 작업을 수행해야 함. 따라서 chain.filter(exchange)를 호출하여 다음 필터를 실행한 후, then 메소드를 사용하여 응답이 완료된 후에 실행할 작업을 정의.
실습
클라우드 게이트웨이 + 유레카 + order 인스턴스 1개 + product 인스턴스 2개로 실습해보기
👉🏻 이전 글에서 사용한 유레카 서버와 order 인스턴스, product 인스턴스 재사용
2024.08.17 - [Spring/MSA] - 클라이언트 사이드 로드 밸런싱 FeignClient와 Ribbon 이해하기
OrderController.java
@GetMapping("/order")
public String getOrder() {
return "Order Details";
}
ProductController.java
@GetMapping("/product")
public String getProduct() {
return "Product info!! From port : " + port;
}
두 컨트롤러의 메소드를 위와 같이 수정해준다.
👉🏻 https://start.spring.io/ 접속해 Gateway 생성하기
위와 같은 의존성 추가하여 gateway 프로젝트 생성 후 Filter 추가
CustomPreFilter.java
@Component
public class CustomPreFilter implements GlobalFilter, Ordered {
private static final Logger logger = Logger.getLogger(CustomPreFilter.class.getName());
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
logger.info("Pre Filter : request URI : " + request.getURI());
return chain.filter(exchange);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
CustomPostFilter.java
@Component
public class CustomPostFilter implements GlobalFilter, Ordered {
private static final Logger logger = Logger.getLogger(CustomPostFilter.class.getName());
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
ServerHttpResponse response = exchange.getResponse();
logger.info("Post Filter : Response status code is " + response.getStatusCode());
}));
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
application.yml
server:
port: 19091 # 게이트웨이 서비스가 실행될 포트 번호
spring:
main:
web-application-type: reactive # Spring 애플리케이션이 리액티브 웹 애플리케이션으로 설정됨
application:
name: gateway-service # 애플리케이션 이름을 'gateway-service'로 설정
cloud:
gateway:
routes: # Spring Cloud Gateway의 라우팅 설정
- id: order-service # 라우트 식별자
uri: lb://order-service # 'order-service'라는 이름으로 로드 밸런싱된 서비스로 라우팅
predicates:
- Path=/order/** # /order/** 경로로 들어오는 요청을 이 라우트로 처리
- id: product-service # 라우트 식별자
uri: lb://product-service # 'product-service'라는 이름으로 로드 밸런싱된 서비스로 라우팅
predicates:
- Path=/product/** # /product/** 경로로 들어오는 요청을 이 라우트로 처리
discovery:
locator:
enabled: true # 서비스 디스커버리를 통해 동적으로 라우트를 생성하도록 설정
eureka:
client:
service-url:
defaultZone: http://localhost:19090/eureka/ # Eureka 서버의 URL을 지정
routes: 밑으로 적혀있는 코드가 게이트웨이에게 어떻게 라우팅을 해야하는지 알려주는 부분이다.
/order/** → 앞에 order를 붙인 요청이 들어오면 모두 order-service로 라우팅하라는 뜻.
이제 게이트웨이를 실행하고 게이트웨이를 통해 order을 요청하면
위와 같은 화면을 확인 할 수 있다.
또한 Product를 요청할 시 요청을 할 때 마다
19093 포트와 19094 포트가 라운드로빈에 의해 번갈아가며 뜨며, 필터에 작성된 로그를 확인하면
다음과 같이 잘 작성됨을 볼 수 있다.
'Spring' 카테고리의 다른 글
Spring 공통 모듈 & 공통 DTO 사용하기 (0) | 2024.10.01 |
---|---|
클라이언트 사이드 로드 밸런싱 FeignClient와 Ribbon 이해하기 (0) | 2024.08.17 |
서비스 디스커버리 Eureka 서버 이해하기 및 실습 (0) | 2024.08.17 |
MSA와 Spring Cloud 이해하기 (0) | 2024.08.17 |
Spring의 RestTemplate 이해하기 (0) | 2024.08.17 |