서버가 새롭게 실행될 때, 서버 주소와 IP 값은 계속 변경된다. 그럴 때도 유레카 클라이언트를 등록해두면, 유레카 서버에서 새롭게 올라온 서버들도 자동으로 관리를 해준다. 그리고 게이트웨이에서는 서버의 수만큼 라운드로빈 방식으로 요청을 분배하여 전달한다.
implementation("org.springframework.cloud:spring-cloud-starter-netflix-eureka-client:4.1.2")
spring-cloud-starter-netflix-eureka-client를 사용했다.
버전 : 4.1.2
server:
port: 0
eureka:
instance:
instance-id: ${spring.cloud.client.hostname}:${spring.application.instance_id:${random.value}}

게이트웨이 서버의 경우에도 유레카 서버가 관리할 수 있도록 등록해준다.
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8761/eureka
그런 다음, RouteConfig에서 유레카 서버에 등록된 각 서비스들의 애플리케이션 이름을 가져와서 uri 부분에 lb://{Application Name}으로 작성한다.
@Configuration
public class RouteConfig {
@Bean
public RouteLocator gatewayRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route("coupon-api",
r -> r.path("/coupon/**")
.filters(f -> f.stripPrefix(1))
.uri("lb://COUPON-API"))
.route("queue-for-reserve",
r -> r.path("/queue/**")
.filters(f -> f.stripPrefix(1))
.uri("lb://QUEUE-FOR-RESERVE"))
.build();
}
}
위와 같이 설정하면, 게이트웨이 서버에서 로드밸런싱을 위한 설정이 완료되었다. 서비스 인스턴스 N개가 UP일 때, 라운드로빈 방식으로 로드 밸런싱이 이루어진다.
토큰 검증의 경우에도 게이트웨이 서버에서 처리한다.
이렇게 되면 장점이 여러가지가 있다.
우선 첫 번째, 뒷 단 애플리케이션 서버에서는 처리해야 할 비즈니스 로직에만 집중할 수 있다.
두 번째, 토큰 검증의 처리 지점이 하나로 통일되어, 에러 발생 시 더 단순하게 원인을 파악할 수 있다.
TokenAuthenticationFilterGlobalFilter를 상속받아서, 모든 요청에 대해 토큰 검증 필터를 거치도록 구현하였다. 들어온 요청은 검증이 완료되었을 경우, 헤더에 이메일 값을 추가하여 전달하는 로직이다.@Component
@Slf4j
public class TokenAuthenticationFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("AuthorizationHeaderFilter called. path : {}", exchange.getRequest().getPath().value());
String jwt = extractJwt(exchange.getRequest());
verifyJwt(jwt);
String email = extractEmail(jwt);
addHeader(exchange, email);
log.info("AuthorizationHeaderFilter ending. path : {}, email : {}", exchange.getRequest().getPath().value(), email);
return chain.filter(exchange);
}
}
LoggingFilterLoggingFilter의 경우, AbstractGatewayFilterFactory를 상속받아 구현하였다. 그렇기 때문에 이 필터는 모든 요청에 대해 동작하는 것은 아니며, 특정 path 형태를 가진 요청에 대해서만 필터를 거치도록 RouteConfig에서 설정하고 커스텀하여 사용할 수 있다.@Component
@Slf4j
public class LoggingFilter extends AbstractGatewayFilterFactory<LoggingFilter.Config> {
@Override
public GatewayFilter apply(Config config) {
return (((exchange, chain) -> {
var request = exchange.getRequest();
var response = exchange.getResponse();
log.info("Logging filter baseMessage. Application Name -> {}", config.getBaseMessage());
if (config.preLogger) {
log.info("Logging filter Start: request id -> {}", request.getId());
}
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
if (config.postLogger) {
log.info("Logging filter End: response code -> {}", response.getStatusCode());
}
}));
}));
}
}