Spring Cloud Gateway(SCG)란?
Spring Cloud Gateway는 Spring 기반의 API Gateway이며 클라이언트와 마이크로서비스 사이의 중계기 역할을 한다.
API 게이트웨이는 마이크로서비스의 앞에서 클라이언트로부터 모든 요청을 일괄적으로 전달받아 처리하기 때문에 엔드포인트를 단일화할 수 있다는 장점이 있다.
위 이미지를 보면 마이크서비스가 현재는 3개가 있지만 새로운 마이크로서비스가 추가되거나 어떤 마이크로서비스에 주소가 변경되었다고 하면 클라이언트 애플리케이션 측에서도 수정하고 배포하는 과정이 추가적으로 필요하다.
이렇다 보니 단일 진입점 형태의 개발이 필요해서 API Gateway를 사용하게 되었다고 한다.
API 게이트웨이를 사용하면 각각의 마이크로서비스로 요청되는 모든 정보에 대해서 API Gateway가 일괄적으로 처리할 수 있고 인증 및 권한부여, 응답 캐싱, 마이크로서비스 통합 검색, 부하 분산, 요청 흐름 추적 등의 장점이 있다.
Spring Cloud Gateway(SCG) 구성
API 게이트웨이 구현체로 오픈소스인 Spring Cloud Netflix의 Zuul이 있었으나 Spring Boot 2.4부터 Maintenance mode(패치 보류)가 되었다.
Spring.io는 Zuul이 중단되고 Spring Cloud Gateway 권장하는데 지금 생각해 보면 그럴 수밖에 없었을 것 같다.
Spring Cloud Gateway
- 비동기, 논블로킹 방식의 Netty 서버
- Spring WebFlux 기반
Spring Cloud Neflix Zuul
- 동기 방식의 Tomcat 서버
- Spring Web MVC 기반
API 게이트웨이는 수많은 요청을 받지만 어려운 일은 하지 않으므로 비동기 방식이 더 적합한 방식이라고 생각된다.
Spring Cloud Gateway 구현
1. API Gateway 프로젝트 생성
나는 프로젝트 이름을 apigateway-service로 생성하였다.
프로젝트 생성 시 위 이미지처럼 아래 3개의 디펜던시를 추가한다.
- Eureka Discovery Client
- Lombok
- Gateway
이번 프로젝트는 Maven 빌드 도구를 사용했는데 intellij에서 Gateway 디펜던시 추가 시 gateway-mvc가 추가되니 직접 수정해야 한다.
<!-- 주의! intellij에서 gateway dependencies 추가 시 gateway-mvc가 추가된다. -->
<!--<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway-mvc</artifactId>
</dependency>-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
2. API Gateway Filter 설정
2.1. API Gateway Filter 설정 - yml
spring:
application:
name: apigateway-service
cloud:
gateway:
routes:
- id: first-service
uri: http://localhost:8081/
predicates:
- Path=/first-service/**
filters:
- AddRequestHeader=first-request, first-request-header2
- AddResponseHeader=first-response, first-response-header2
- id: second-service
uri: http://localhost:8082/
predicates:
- Path=/second-service/**
filters:
- AddRequestHeader=second-request, second-request-header2
- AddResponseHeader=second-response, second-response-header2
- spring.cloud.gateway.routes 속성 설명
- id: microservice 이름
- uri: 라우팅 목적지
- predicates: 요청을 처리하기 전 설정한 정보와 HTTP 요청이 부합하는지 체크함(조건절)
- Path=/first-service/**: first-service 하위로 들어온 모든 요청을 수용함
- filters: API Gateway에서 받은 요청과 응답을 수정할 수 있음
2.2. API Gateway Filter 설정 - java bean
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* spring cloud gateway 프로퍼티 설정
*/
@Configuration
public class FilterConfig {
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route(predicateSpec -> predicateSpec.path("/first-service/**")
.filters(gatewayFilterSpec -> gatewayFilterSpec.addRequestHeader("first-request", "first-request-header")
.addResponseHeader("first-response", "first-response-header"))
.uri("http://localhost:8081/"))
.route(predicateSpec -> predicateSpec.path("/second-service/**")
.filters(gatewayFilterSpec -> gatewayFilterSpec.addRequestHeader("second-request", "second-request-header")
.addResponseHeader("second-response", "second-response-header"))
.uri("http://localhost:8082/"))
.build();
}
}
yml 또는 java object로 API Gateway Filter 정보를 설정할 수 있으며 여기까지 진행하였다면 간단한 API Gateway Filter 세팅이 완료된 것이다.
이후 apigateway-service의 서버를 기동하면 Netty 서버가 기동 된 것을 확인할 수 있다.
2.3. API Gateway 테스트
현재까지 만들어진 서비스는 4개이며 위에서 만든 API Gateway service가 apigateway-service이다.
- discovery-service (port: 8761)
- first-service (port: 8081)
- second-service (port: 8082)
- apigateway-service (port: 8000)
API Gateway 테스트를 진행하기 위해 first-service와 second-service에 컨트롤러를 생성한다.
- first-service controller
@Slf4j
@RestController
@RequestMapping("/first-service")
public class FirstServiceController {
@GetMapping("/welcome")
public String welcome() {
return "Welcome to the first service. ";
}
@GetMapping("/message")
public String message(@RequestHeader("first-request") String header) {
log.info("header value = {}", header);
return "Hello World in First Service";
}
}
- second-service controller
@Slf4j
@RestController
@RequestMapping("/second-service")
public class SecondServiceController {
@GetMapping("/welcome")
public String welcome() {
return "Welcome to the second service. ";
}
@GetMapping("/message")
public String message(@RequestHeader("second-request") String header) {
log.info("header value = {}", header);
return "Hello World in Second Service";
}
}
위와 같이 controller를 생성한 후 각 서비스의 부트 앱을 기동 시키고 크롬에서 아래와 같이 요청을 해보겠다.
- http://127.0.0.1:8000/first-service/message
- http://127.0.0.1:8000/second-service/message
http://127.0.0.1:8000/first-service/message 요청 결과
apigateway-service로 요청을 하였지만 first-service 서비스에 있는 API 응답이 반환된 것을 확인할 수 있다.
크롬 관리자 도구에서 헤더 탭을 보면 apigateway-service에서 설정한 response header 값이 들어간 것을 확인할 수 있다.
http://127.0.0.1:8000/second-service/message 요청 결과
위 first-service에 대한 요청 결과와 동일하게 apigateway-service에서 설정한 response header 값이 들어간 것을 확인할 수 있다.
여기까지 간단한 API Gateway 구현 및 Filter 세팅이 완료된 것이다.
'MSA' 카테고리의 다른 글
[MSA] Spring Cloud Gateway(SCG)에서 Rate Limiter 구현 (feat, redis & docker) (1) | 2024.12.07 |
---|---|
[MSA] Cloud Native Architecture, Application란 (0) | 2024.04.28 |