반응형
Spring Cloud Gateway Filter 적용
- Spring Cloud Gateway는 다음과 같이 마이크로서비스들과 클라이언트단의 라우팅 역할을 해준다. 이때
Filter
기능을 통해 조건을 분기한다. 사전에 정의하는PreFilter
와 사후에 정의하는PostFilter
가 있다. Filter 정의는 Java 코드 상에서 사용하거나, Property에 정의할 수 있다.
동작 과정
- 동작 흐름은 다음과 같이 이루어진다. 최초의 Request시에 Request-Header에 서비스명을 넣어 로직을 처리해주고, 마지막 Response 시에도 마찬가지로 Response-Header에 서비스명을 넣어 요청에 맞는 클라이언트에게 반환주는 방식이다.
- 공식문서에는 다음과 같이 정의되어 있다. 필터를 통해
Proxied Service
를 기점으로 프록시를 보내기 전인 Pre Filter와 보낸 후인 Post Filter를 구별할 수 있다고한다.
필터 적용 - 빈 등록
@RestController
@RequestMapping("/first-service")
@Slf4j
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);
return "Hello World in First Service. ";
}
}
- 서비스 단의 컨트롤러는 다음과 같이
@RequestHeader
를 지정해서, 요청 핸들링을 할 수 있도록 처리하자.
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;
@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();
}
}
RouterLocator
를 빈으로 등록해서 두 개의 서비스를 라우팅 처리해주자.
- 다음과 같이 서비스 요청이 정상 동작하는 것을 볼 수 있다.
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-resolver-dns-native-macos</artifactId>
<version>4.1.79.Final</version>
<classifier>osx-aarch_64</classifier>
</dependency>
- 만약 M1 맥 환경에서 netty_resolver_dns 에러가 발생한다면 다음과 같이
osx-aarch_64
라이브러리를 추가해주자.
필터 적용 - 프로퍼티
- 자바 코드 단에서 빈 등록하지 않고, 프로퍼티 설정으로 필터를 적용할 수도 있다.
필터 적용 - 커스텀
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
@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();
ServerHttpResponse response = exchange.getResponse();
log.info("Custom PRE filter : request id -> {}", request.getId());
// Custom Post Filter - Mono는 Web Flux 로 Spring 5부터 제공
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("Custom POST filter : response code -> {}", response.getStatusCode());
}));
});
}
public static class Config {
// Put the Configuration properties
}
}
AbstractGatewayFilterFactory
를 상속받아서apply
를 구현하여 커스텀 필터 처리를 할 수 있다.
server:
port: 8000
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/**
filters:
- CustomFilter
- id: second-service
uri: http://localhost:8082
predicates:
- Path=/second-service/**
filters:
- CustomFilter
- application.yml은 다음과 같이 설정한다.
Eureka 연동
- Eureka 서버는 다음과 같이 Service Discovery와 마이크로 서비스 등록역할을 한다.
- 예를들어
FIRST SERVICE
에 접근하기 위하여http://localhost:8000/first-service/welcome
이라는 요청이 오면, API Gateway가 이 요청을 받고 Eureka 서버에 접근한다. 서버를 통해 마이크로서버 정보를 가져온 후 해당 서비스에 요청을 전달한다.
- 예를들어
application.yml 설정
server:
port: 8761
spring:
application:
name: discoveryservice
eureka:
client:
# eureka의 registry에 등록할지 여부 설정
register-with-eureka: false
# eureka의 registry에 있는 정보를 가져올지 여부 설정
fetch-registry: false
- 유레카 서버는 다음과 같이 설정되어 있다.
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8761/eureka
- 마이크로 서비스들은 유레카 서버에 등록하기 위해 true로 설정해줘야 한다.
server:
port: 8000
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8761/eureka
spring:
application:
name: apigateway-service
cloud:
gateway:
default-filters:
- name: GlobalFilter
args:
baseMessage: Spring Cloud Gateway Filter
preLogger: true
postLogger: true
routes:
- id: first-service
uri: lb://MY-FIRST-SERVICE
predicates:
- Path=/first-service/**
filters:
- CustomFilter
- id: lb://MY-SECOND-SERVICE
uri: http://localhost:8082
predicates:
- Path=/second-service/**
filters:
- name: CustomFilter
- name: LoggingFilter
args:
baseMessage: Hi, there
preLogger: true
postLogger: true
- API Gateway는 다음과 같이 라우팅할 마이크로서비스 정보를 지정하고 있다. 이때 routes 부분을 보면
lb
를 통해 지정된 uri가 보일 것이다. 이는 유레카 서버의 로드 밸런싱 기능을 의미한다.
- 유레카 서버를 접속해보면 등록된 서비스 정보를 볼 수 있다.
lb://Application명
으로 지정하는 것을 볼 수 있다.
server:
port: 0
spring:
application:
name: my-first-service
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8761/eureka
instance:
instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}};
- 만약 랜덤포트를 통해 각 마이크로서비스를 이중화 시키고 싶을 경우 다음과 같이 port를 0으로 설정하고, instance에서 random value를 설정하면 된다.
REFERENCE
https://cloud.spring.io/spring-cloud-gateway/reference/html/
반응형