기존의 Netfilx Zuul의 단점 : 비동기/Functional 지원 X 또는 스프링 라이브러리와 호환성 문제
Spring Cloud Gateway
- 비동기 서비스 지원
- 서블릿 X -> 기존에 사용하던 ServletRequest/Response 쓰지 않고 ServerRequest/Response 사용
1. Spring Cloud Gateway 서버 구성하기
application.yml
server:
port: 8080
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defaultZone: http:localhost:8761/eureka
spring:
application:
name: apigateway-service
cloud:
gateway:
routes:
- id: first-service
uri: http://localhost:8081/
predicates:
- Path=/first-service/**
- id: second-service
uri: http://localhost:8082/
predicates:
- Path=/second-service/**
서버를 띄우면 다음과 같은 로그를 확인할 수 있다.
2025-01-06T13:40:59.846+09:00 INFO 9740 --- [apigateway-service] [ main] o.s.b.web.embedded.netty.NettyWebServer : Netty started on port 8080 (http)
이전의 netfilx zuul을 사용할 때는 tomcat 내장서버를 사용하여 동기로 처리되었는데, spring cloud gateway를 사용하면 netty 비동기 내장 서버를 사용하게 된다. => 비동기방식으로 작동
단, 주의할 점은 spring boot 3점대 이상 기준 spring initilizer로 프로젝트를 생성하면 spring-cloud-starter-gateway-mvc 의존성을 가져오게되는데, spring-cloud-starter-gateway 의존성을 가져와야 정상적으로 Netty서버로 실행된다. (아니면 그냥 tomcat 서버 실행)
2. Filter 적용하기 - 설정 Config하기
1) Java코드로 설정하기 - bean등
@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();
}
}
클라이언트가 gateway의 서버 주소인 localhost:8000으로 위의 FilterConfig에서 라우팅 설정한 대로 요청하게 되면( localhost:8000/first-service/...) 서비스 요청 전/후로 requestheader와 responseheader를 추가해서 해당 uri로 요청/응답한다.
2) 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-requests-header2
- AddResponseHeader=first-response, first-response-header2
- id: second-service
uri: http://localhost:8082/
predicates:
- Path=/second-service/**
filters:
- AddRequestHeader=second-request, second-requests-header2
- AddResponseHeader=second-response, second-response-header2
3. Filter 적용하기 - Filter 생성하기 (1) Custom Filter
AbstractGatewayFilterFactory 상속
@Component
@Slf4j
public class CustomFilter extends AbstractGatewayFilterFactory<Object> {
@Override
public GatewayFilter apply(Object config) {
// Custom Pre Filter
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
log.info("Custom Pre Filter : request id = {}", request.getId());
// Custom Post Filter
return chain.filter(exchange)
.then(Mono.fromRunnable(() -> {
log.info("Custom Post Filter : response code = {}", response.getStatusCode());
}));
};
}
}
- Pre Filter :
request, response 정보 받기 -> servlet을 사용하지 않기 때문에 server request/response를 받아온다.
- Post Filter :
chain에서 filter를 반환 시키면서 사후의 동작을 설정
Mono : 웹플럭스(Spring 5에서 추가) -> 동기 방식이 아닌 비동기방식의 서버를 지원할 때 단일값 전달
- application.yml에 custom filter 등록
spring:
application:
name: apigateway-service
cloud:
gateway:
routes:
- id: first-service
uri: http://localhost:8081/
predicates:
- Path=/first-service/**
filters:
- CustomeFilter
- id: second-service
uri: http://localhost:8082/
predicates:
- Path=/second-service/**
filters:
- CustomeFilter
라우트 정보가 있을 때, 라우트 정보마다 무조건 사용하는 필터
- 결과
2025-01-06T14:59:11.805+09:00 INFO 26704 --- [apigateway-service] [ctor-http-nio-3] c.e.a.filter.CustomFilter : Custom Pre Filter : request id = cf36bbf9-2
2025-01-06T14:59:11.927+09:00 INFO 26704 --- [apigateway-service] [ctor-http-nio-3] c.e.a.filter.CustomFilter : Custom Post Filter : response code = 200 OK
3. Filter 적용하기 - Filter 생성하기 (2) Global Filter
- 요구사항 : CustomeFilter + Global Filter 추가 (설정값 추가)
application.yml
spring:
application:
name: apigateway-service
cloud:
gateway:
default-filters:
- name: GlobalFilter
args:
baseMessage: Spring Cloud Gateway Global Filter
preLogger: true
postLogger: true
routes:
- id: first-service
uri: http://localhost:8081/
predicates:
- Path=/first-service/**
filters:
- CustomFilter
- id: second-service
uri: http://localhost:8082/
predicates:
- Path=/second-service/**
filters:
- CustomFilter
Global Filter Java코드
@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 Filter baseMessage : {}", config.getBaseMessage());
if(config.isPreLogger()) {
log.info("Global Filter 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;
}
}
로그 확인
2025-01-06T15:25:15.632+09:00 INFO 10884 --- [apigateway-service] [ctor-http-nio-3] c.e.a.filter.GlobalFilter : Global Filter baseMessage : Spring Cloud Gateway Global Filter
2025-01-06T15:25:15.632+09:00 INFO 10884 --- [apigateway-service] [ctor-http-nio-3] c.e.a.filter.GlobalFilter : Global Filter Start: request id -> 28aa7bd8-1
2025-01-06T15:25:15.633+09:00 INFO 10884 --- [apigateway-service] [ctor-http-nio-3] c.e.a.filter.CustomFilter : Custom Pre Filter : request id = 28aa7bd8-1
2025-01-06T15:25:15.854+09:00 INFO 10884 --- [apigateway-service] [ctor-http-nio-3] c.e.a.filter.CustomFilter : Custom Post Filter : response code = 200 OK
2025-01-06T15:25:15.854+09:00 INFO 10884 --- [apigateway-service] [ctor-http-nio-3] c.e.a.filter.GlobalFilter : Global Filter End: response code -> 200 OK
Global Filter -> Custom Filter 순
3. Filter 적용하기 - Filter 생성하기 (3) Logging Filter
Custom Filter를 응용해서 Loggin Filter 만들기
요구사항 : first-service, second-service가 있을때 global filter를 전역으로 설정하고, custom filter도 두 가지 서비스에 모두 적용하고, loggin filter는 second-service에만 등록하기
순서 : Gloabl Filter -> Custom Filter -> Loggin Filter 순
application.yml
spring:
application:
name: apigateway-service
cloud:
gateway:
default-filters:
- name: GlobalFilter
args:
baseMessage: Spring Cloud Gateway Global Filter
preLogger: true
postLogger: true
routes:
- id: first-service
uri: http://localhost:8081/
predicates:
- Path=/first-service/**
filters:
- CustomFilter
- id: second-service
uri: http://localhost:8082/
predicates:
- Path=/second-service/**
filters:
- CustomFilter
- name: LoggingFilter
args:
baseMessage: Hi, there.
preLogger: true
postLogger: true
LogginFilter
@Component
@Slf4j
public class LoggingFilter extends AbstractGatewayFilterFactory<LoggingFilter.Config> {
public LoggingFilter() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
GatewayFilter filter = new OrderedGatewayFilter((exchange, chain) -> {
// Custom Pre Filter
ServerHttpRequest request = exchange.getRequest();
ServerHttpResponse response = exchange.getResponse();
log.info("Logging Filter baseMessage : {}", config.getBaseMessage());
if(config.isPreLogger()) {
log.info("Logging Pre Filter: request id -> {}", request.getId());
}
// Custom Post Filter
return chain.filter(exchange)
.then(Mono.fromRunnable(() -> {
if (config.isPostLogger()) {
log.info("Logging Post Filter: response code -> {}", response.getStatusCode());
}
}));
}, Ordered.HIGHEST_PRECEDENCE);
return filter;
}
@Data
public static class Config {
private String baseMessage;
private boolean preLogger;
private boolean postLogger;
}
}
로그 확인
first-service 호출 시
2025-01-06T15:42:37.351+09:00 INFO 19208 --- [apigateway-service] [ctor-http-nio-3] c.e.a.filter.GlobalFilter : Global Filter baseMessage : Spring Cloud Gateway Global Filter
2025-01-06T15:42:37.351+09:00 INFO 19208 --- [apigateway-service] [ctor-http-nio-3] c.e.a.filter.GlobalFilter : Global Filter Start: request id -> 24a063a7-1
2025-01-06T15:42:37.352+09:00 INFO 19208 --- [apigateway-service] [ctor-http-nio-3] c.e.a.filter.CustomFilter : Custom Pre Filter : request id = 24a063a7-1
2025-01-06T15:42:37.495+09:00 INFO 19208 --- [apigateway-service] [ctor-http-nio-3] c.e.a.filter.CustomFilter : Custom Post Filter : response code = 200 OK
2025-01-06T15:42:37.495+09:00 INFO 19208 --- [apigateway-service] [ctor-http-nio-3] c.e.a.filter.GlobalFilter : Global Filter End: response code -> 200 OK
Logging Filter 호출x
second-service 호출 시
2025-01-06T15:43:14.327+09:00 INFO 19208 --- [apigateway-service] [ctor-http-nio-3] c.e.a.filter.LoggingFilter : Logging Filter baseMessage : Hi, there.
2025-01-06T15:43:14.328+09:00 INFO 19208 --- [apigateway-service] [ctor-http-nio-3] c.e.a.filter.LoggingFilter : Logging Pre Filter: request id -> 24a063a7-2
2025-01-06T15:43:14.328+09:00 INFO 19208 --- [apigateway-service] [ctor-http-nio-3] c.e.a.filter.GlobalFilter : Global Filter baseMessage : Spring Cloud Gateway Global Filter
2025-01-06T15:43:14.328+09:00 INFO 19208 --- [apigateway-service] [ctor-http-nio-3] c.e.a.filter.GlobalFilter : Global Filter Start: request id -> 24a063a7-2
2025-01-06T15:43:14.328+09:00 INFO 19208 --- [apigateway-service] [ctor-http-nio-3] c.e.a.filter.CustomFilter : Custom Pre Filter : request id = 24a063a7-2
2025-01-06T15:43:14.384+09:00 INFO 19208 --- [apigateway-service] [ctor-http-nio-3] c.e.a.filter.CustomFilter : Custom Post Filter : response code = 200 OK
2025-01-06T15:43:14.384+09:00 INFO 19208 --- [apigateway-service] [ctor-http-nio-3] c.e.a.filter.GlobalFilter : Global Filter End: response code -> 200 OK
2025-01-06T15:43:14.385+09:00 INFO 19208 --- [apigateway-service] [ctor-http-nio-3] c.e.a.filter.LoggingFilter : Logging Post Filter: response code -> 200 OK
Logging Filter 호출 O
HIGHEST 순서 설정 때문에 Logging -> Global -> Custom 순서가 되엇음
LOWEST로 설정하면 Global -> Custom -> Logging 순으로 만들 수 있음
'백엔드 Backend > 서버 Server' 카테고리의 다른 글
Spring Cloud Config - 분산 시스템 설정 관리 (0) | 2025.01.14 |
---|---|
Eureka + Spring Cloud Gateway 구현하기 (0) | 2025.01.07 |
API 게이트웨이 서비스 - (1) Netfilx Zuul (1) | 2025.01.06 |
Spring Boot 프로젝트를 Docker 이미지로 빌드하기 (1) | 2024.08.02 |
Git + Jenkins + Docker로 서버와 CI/CD구축하기 (0) | 2024.08.02 |