API Gateway 대표 라이브러리
- Netflix Zuul
- Spring Cloud Gateway
Netflix Zuul
- 넷플릭스 OSS(오픈소스 소프트웨어) 스택의 일부로 개발된 API Gateway
- 주로 MSA에서 사용되며 다양한 트래픽 라우팅, 필터링 및 로드 밸런싱 기능을 제공
Netflix Zuul의 특징
- 1세대 API Gateway
- Zuul은 기본적으로 HTTP 요청 라우팅과 필터링에 중점을 둔 1세대 API Gateway
- Spring Cloud에서 Zuul 1을 사용하는 경우, 내부적으로 블로킹 방식으로 동작
- 필터 기반 아키텍처
- Zuul은 요청을 처리하는 여러 필터를 지원
- 필터를 사용해 요청 전/후의 다양한 기능(인증, 로깅, 트랜잭션 관리 등)을 추가 가능
- 확장성
- Zuul은 기능 확장성이 뛰어나지만 JVM 기반으로 블로킹 I/O 모델을 사용하기 때문에 고성능이 필요한 대규모 시스템에는 성능이 제한될 수 있음
- 넷플릭스 OSS 지원 종료
- 넷플릭스는 Zuul 1에 대한 공식 지원을 종료
- 따라서 유지보수와 새로운 기능 추가가 제한적
- 현재는 Zuul 2로의 전환을 권장하지만 Zuul 2는 Spring Cloud와의 완전한 통합이 이루어지지 않아 사용하기가 어려움
Spring Cloud Gateway
- Spring Cloud Gateway는 스프링 생태계를 위한 API Gateway로 Zuul의 단점을 개선한 비동기, 논블로킹 API Gateway
- 스프링 5와 리액티브 프로그래밍을 기반으로 개발
Spring Cloud Gateway 특징
- 비동기 및 논블로킹
- Spring Cloud Gateway는 Netty 기반으로 설계되어 비동기 논블로킹 방식으로 동작
- 이를 통해 대규모 트래픽을 효율적으로 처리 가능
- Spring과의 완벽한 통합
- Spring Cloud Gateway는 Spring Boot와 Spring Cloud와 완벽히 통합
- Spring 생태계를 사용하는 개발자들에게 친숙한 설정 방식과 코드 구조를 제공
- 라우팅 및 필터 기능
- 다양한 필터(사전/사후 필터)를 지원하며, 라우팅과 필터 로직을 간편하게 설정 가능
- 예를 들어, 경로 기반 라우팅, 헤더 기반 라우팅, 권한 검증 등을 손쉽게 구성 가능
- 향후 지원 및 커뮤니티
- Spring 프로젝트는 활발히 유지보수되고 있으며, 새로운 기능과 보안 패치가 지속적으로 제공
- 또한, 넷플릭스 OSS의 많은 기능들이 Spring Cloud로 대체되었기 때문에 향후 유지보수 측면에서도 안정적
라이브러리 선택
결론적으로는 Spring Cloud Gateway를 사용하는 것을 권장
- 비동기 및 고성능 처리
- Spring Cloud Gateway는 비동기 논블로킹 구조로 설계되어 있어 성능이 뛰어나고 대규모 트래픽 처리에 유리
- Spring 생태계와의 완벽한 통합
- Spring Boot 및 Spring Cloud를 사용하는 프로젝트에서는 Spring Cloud Gateway를 통해 간편하게 API Gateway를 구성 가능
- 커뮤니티 지원 및 유지보수
- Spring Cloud Gateway는 지속적으로 개선되고 있으며, 넷플릭스 Zuul 1은 더 이상 공식 지원이 없는 상태
결론
- Zuul을 고려할 수 있는 경우는 기존에 넷플릭스 OSS 스택을 사용 중이거나 특정 비즈니스 요구사항 때문에 Zuul의 필터 아키텍처를 선호하는 경우
- 그러나 신규 프로젝트나 장기적인 관점에서는 Spring Cloud Gateway가 더 적합한 선택
블록킹I/O는 요청이 발생하면 해당 요청이 끝날 때까지 스레드가 기다리는 방식으로 응답이 올 때까지 스레드가 차단(Block) 되는 방식이기 때문에 비효율적인 자원 사용, 스레드 수의 한계, 컨텍스트 스위칭 오버헤드를 이유로 문제가 발생한다고 함
추후 블록킹 I/O, 논블록킹 I/O, Context Switching에 대해 더 알아보도록 할 것
API Gateway 사용 예제
1. API Gateway로 사용할 모듈 생성 및 settings.gradle 추가
rootProject.name = 'playheaven'
include 'member-service'
include 'order-service'
include 'game-service'
include 'eureka-server'
include 'api-gateway'
2. 의존성 추가
- API Gateway 또한 Eureka Client로 등록해서 Intstance 정보들을 Fetch 받아야 서비스의 정보(IP주소, 포트)가 변경되거나 새로운 인스턴스가 추가 되었을 때 자동으로 인식할 뿐만 아니라, Eureka Server에서 가지고 온 서비스 인스턴스 목록을 사용하여 로드 밸런싱을 해줄 수 있음
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
주의사항
- IntelliJ Starters에서 Gateway를 검색하면 Spring Cloud Routing에 Gateway와 Reactive Gateway 두 가지가 나옴
- 반드시 Reactive Gateway를 선택해야 하고 그냥 Gateway 의존성을 추가하면 아래 처럼 끝에 mvc가 붙게 됨
- 그렇게 되면 아래 사진 처럼, spring.cloude.gateway.routes를 인식할 수 없게 되기 때문에 주의
implementation 'org.springframework.cloud:spring-cloud-starter-gateway-mvc'
3. Eureka Client 활성화 (작성하지 않아도 자동 인식되긴 함)
@SpringBootApplication
@EnableDiscoveryClient
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class, args);
}
}
4. 라우팅 설정 (api-gateway의 application.yml파일)
server:
port: 9999
spring:
application:
name: api-gateway
cloud:
gateway:
routes:
- id: member-service
uri: lb://member-service
predicates:
- Path=/member-service/**
filters:
- StripPrefix=1
- id: order-service
uri: lb://order-service
predicates:
- Path=/order-service/**
filters:
- StripPrefix=1
- id: game-service
uri: lb://game-service
predicates:
- Path=/game-service/**
filters:
- StripPrefix=1
eureka:
instance:
prefer-ip-address: true
client:
register-with-eureka: true
fetch-registry: true
serviceUrl:
defaultZone: http://localhost:8888/eureka
- spring.cloud.gateway.routes.id
- 라우팅을 구분하기 위한 route id를 지정
- spring.application.name에 지정한 것과 관계 없음
- 각 라우트에 대해 고유한 id를 지정함으로써 등록, 업데이트 ,삭제 등의 관리에 사용
- spring.cloud.gateway.routes.uri
- 요청이 라우팅되는 경로 URI를 의미
- lb 프로토콜(lb://)의 경로를 지정하면 Eureka Server에서 호스트에 해당하는 서비스를 찾고 로드밸런싱을 수행
- Ex) lb://member-service라고 한다면, routes.id가 아니라 spring.application.name이 member-service로 Eureka Server에 등록된 서비스를 찾아 로드밸런싱
- spring.cloud.gateway.routes.predicates
- 요청 URI를 기반으로 라우팅 조건을 정의
예를 들어, Path=/member-service/**는 /member-service/로 시작하는 모든 요청을 이 라우트로 매핑 - Postman 요청: http://아이피주소:APIGateway포트/member-service/api/member/list라는 요청이 들어오면, 경로가 /member-service/**로 설정된 라우트가 이 요청을 처리
- 매핑: MemberController의 @RequestMapping("/api/member")로 정의된 엔드포인트와 매칭
- 요청 URI를 기반으로 라우팅 조건을 정의
- spring.cloud.gateway.routes.filters.StripPrefix
- 요청 URL의 시작부분부터 지정된 수의 경로 세그먼트를 제거하는 접두사 제거 명령
- 요청 URI : http://localhost:8080/member-service/api/member/list
- 필터 설정 : StripPrefix=1
- 필터 적용 후
- 변경된 URI : http://localhost:8080/api/member/list
- member-service 제거, 나머지 경로가 백엔드 서비스로 전달
@RequestMapping("/api/member")
public class MemberController {
주의 사항
- StripPrefix=1 사이에 공백이 있으면 에러 발생
- java.lang.IllegalArgumentException: Unable to find GatewayFilterFactory with name StripPrefix
spring:
application:
name: api-gateway
cloud:
gateway:
routes:
- id: member-service
uri: lb://member-service
predicates:
- Path=/member-service/**
filters:
- StripPrefix = 1
5. Postman 테스트 및 결과
@RestController
@RequestMapping("/api/mypage/wishlist")
@RequiredArgsConstructor
public class WishlistController {
private final WishlistService wishlistService;
@GetMapping("/list/{memberId}")
public ResponseEntity<WishlistResponseDto> list(@PathVariable(name = "memberId") Long memberId,
@RequestParam(name = "pageNo", defaultValue = "1") int pageNo,
@RequestParam(name = "size",defaultValue = "10")int size){
}
'MicroService Architecture' 카테고리의 다른 글
[API Gateway] API Gateway Filter (0) | 2024.08.28 |
---|---|
FeignClient(OpenFeign) 사용 이유, 예제 (0) | 2024.08.21 |
Feign Client / Web Client / RestTemplate (0) | 2024.08.21 |
[Spring Cloud] Netflix Eureka 개념, 용어 정리 및 사용 예제 (0) | 2024.08.20 |
Service Discovery 개념, 종류, 특징 (0) | 2024.08.20 |