클라이언트와 백엔드 사이에 위치 한 API 관리 툴로 내부의 여러 API들에 대한 통합된 하나의 엔트리 포인트를 제공한다. 중간에 위치해서 클라이언트의 특정 API에 대한 요청을 라우팅해주는 역할을 수행한다.

RestTemplate
Feign Client
Netflix Ribbon (현재 버전 종료)
서버에서 실제 gateway처럼 클라이언트의 요청을 받아내는 역할을 수행하는 서비스이다. 하지만 현재는 Spring Cloud Gateway로 대체하여 사용하는 것을 권장하고 있고 Spring boot 2.3까지의 버전만 지원하고 있다.

Pre filter
Routing filter
Post filter
Error filter
자바로 개발되었으며 Servlet 스택 위에 구축되었는데 서블릿 기반의 동기 I/O 방식을 사용하고 있기 때문에 비동기 및 논블로킹 처리가 불가능합니다. 이에 반해 Spring Cloud Gateway는 Spring 프레임워크를 기반으로 개발되었으며, 스프링 리액티브 스택 위에 구축되어 비동기 및 논블로킹 처리를 지원하기 때문에 zuul이 밀렸다고 생각된다.
@Configuration
public class FilterConfig {
@Bean
public RouteLocator gatewayRoutes(RouteLocatorBuilder builder){
return builder.routes()
.route(r -> r.path("/first-service/**")
.filters(f -> f.addRequestHeader("first-request", "first-request-header")
.addResponseHeader("first-response", "first-response-header"))
.uri("http://localhost:8081"))
.route(r -> r.path("/second-service/**")
.filters(f -> f.addRequestHeader("second-request", "second-request-header")
.addResponseHeader("second-response", "second-response-header"))
.uri("http://localhost:8082"))
.build();
}
}
yml에서 설정할 수 있는 정보를 Java 코드를 이용하여 라우팅해줄 수 있다. 하는 과정이 다소 번거롭기 때문에 yml에 설정하는 것이 전역적으로 관리하기 편하여 Java code로 하는 것은 비추천하는 방식이다.
spring:
application:
name: apigateway-service
cloud:
gateway:
routes:
- id: first-service
uri: http://localhost:8001
predicates:
- Path=/first-service/**
filters:
- AddRequestHeader=first-request, first-requests-header2
- AddResponseHeader=first-response, first-response-header2
- id: second-service
uri: http://localhost:8002
predicates:
- Path=/second-service/**
filters:
- AddRequestHeader=second-request, second-requests-header2
- AddResponseHeader=second-response, second-response-header2
AddRequestHeader와 AddResponseHeader에 key-value 형식으로 값을 넣어준다.
@Component
@Slf4j
public class CustomFilter extends AbstractGatewayFilterFactory<CustomFilter.Config> {
public CustomFilter() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
// custom pre filter
return ((exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest(); // ServerHttpRequest 비동기 방식
ServerHttpResponse response = exchange.getResponse();
log.info("Custom pre filter = {}", request.getId());
// custom post filter
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("Custom POST filter = {}", response.getStatusCode());
}));
});
}
public static class Config {
// config 정보 입력
}
}
AbstractGatewayFilterFactory는 Spring Cloud Gateway의 필터를 구현하기 위한 추상클래스이며 사전 필터(pre-filter)는 클라이언트의 요청을 가로채서 수정할 수 있고, 사후 필터(post-filter)는 서버의 응답을 가로채서 수정할 수 있다.
spring:
application:
name: apigateway-service
cloud:
gateway:
routes:
- id: first-service
uri: http://localhost:8001
predicates:
- Path=/first-service/**
filters:
- CustomFilter
- id: second-service
uri: http://localhost:8002
predicates:
- Path=/second-service/**
filters:
- CustomFilter
원래 복잡하게 설정했던 필터를 우리가 빈으로 설정한 CustomFilter를 통하여 쉽게 정의할 수 있고 서비스에 맞는 필터를 적용할 수 있다. (로깅, 보안 검사, 인증, 인가 등)
@Component
@Slf4j
public class GlobalFilter extends AbstractGatewayFilterFactory<GlobalFilter.Config> {
public GlobalFilter() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
// custom pre filter
return ((exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
log.info("Global pre basesMessage = {}", config.getBaseMessage());
if (config.isPreLogger()){
log.info("Global pre start - request id = {}", request.getId());
}
// custom post filter
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
if (config.isPostLogger()){
log.info("Global Filter End : response code = {}", response.getStatusCode());
}
}));
});
}
@Data
public static class Config {
private String baseMessage;
private boolean preLogger;
private boolean PostLogger;
}
}
GlobalFilter는 모든 서비스에 적용되어야 하는 필터의 경우 설정할 수 있다.
spring:
application:
name: apigateway-service
cloud:
gateway:
default-filters:
- name: GlobalFilter
args:
baseMessage: Spring Cloud Gateway Global Filter
preLogger: true
postLogger: true
CustomFilter처럼 각 서비스에 등록하는게 아니라 전역에서 처리하기 위하여 default-filters를 적용해 준다.
기존에는 uri: http://localhost:8001을 입력하여 서비스를 찾았는데 랜덤 포트를 적용한다면 이렇게 할 수 없는 문제가 생긴다. 해결방법으로는 eureka 서버에 등록된 application 이름으로 찾아서 등록해주면 된다.
cloud:
gateway:
default-filters:
- name: GlobalFilter
args:
baseMessage: Spring Cloud Gateway Global Filter
preLogger: true
postLogger: true
routes:
- id: first-service
uri: lb://MY-FIRST-SERVICE
predicates:
- Path=/first-service/**
- id: second-service
uri: lb://MY-SECOND-SERVICE
predicates:
- Path=/second-service/**