마이크로서비스가 수백 대 있어요 이런 마이크로서비스는 공통점이 많죠
인증, 승인, 로깅, 환율 제한 등등이요
이런 공통 기능은 어디서 구현할까요?
전형적인 솔루션은 API 게이트웨이죠
이전에는 Zuul이라는 게이트웨이가 인기가 많았고 많이들 사용했지만
Netflix가 더이상 지원하지 않으면서 Spring Cloud API Gateway를 사용하게 됐어요
spring.application.name=api-gateway
server.port=8765
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka
CURRENCY-EXCHANGE
이 이름의 마이크로 인스턴스 서비스 이름을 찾아서 /currency-exchange/from/USD/to/INR
이 요청을 실행하게 하도록 하길 원함spring.application.name=api-gateway
server.port=8765
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka
spring.cloud.gateway.discovery.locator.enabled=true // 추가
이렇게 공통 기능을 사용하게할 수 있습니다.
NAV에서 어떤 마이크로 서비스도 호출할 수 있죠
API 게이트웨이를 통해 유레카와 함께 등록돼 있어요
인증 같은 걸 구현하고 싶다면 API 게이트웨이에서 구현하면 돼요
마이크로 서비스에서 인증된 것만 허용하면 되죠
모든 인증 논리는 API 게이트웨이에 구현될 수 있죠
따라서 모든 요청은 게이트웨이를 통하여 들어가서 각각의 microservice로 리다이렉트 됨
spring.application.name=api-gateway
server.port=8765
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka
spring.cloud.gateway.discovery.locator.enabled=true
spring.cloud.gateway.discovery.locator.lower-case-service-id=true // 추가
/get
경로로 요청하면 http://httpbin.org:80로 리다이렉트 하게 함@Configuration
public class ApiGateConfiguration {
@Bean
public RouteLocator gatewayRoute(RouteLocatorBuilder builder) {
Function<PredicateSpec, Buildable<Route>> routeFunction
= p -> p.path("/get") // predicateSpec
.uri("http://httpbin.org:80"); //redirect to
return builder.routes()
.route(routeFunction)
.build();
}
}
@Configuration
public class ApiGateConfiguration {
@Bean
public RouteLocator gatewayRoute(RouteLocatorBuilder builder) {
Function<PredicateSpec, Buildable<Route>> routeFunction
= p -> p.path("/get")
.filters(f -> f // 추가
.addRequestHeader("MyHeader", "MyURI") // 추가
.addRequestParameter("Param", "MyValue"))// 추가
.uri("http://httpbin.org:80"); //redirect to
return builder.routes()
.route(routeFunction)
.build();
}
}
@Configuration
public class ApiGateConfiguration {
@Bean
public RouteLocator gatewayRoute(RouteLocatorBuilder builder) {
return builder.routes()
.route(p -> p.path("/get")
.filters(f -> f
.addRequestHeader("MyHeader", "MyURI")
.addRequestParameter("Param", "MyValue"))
.uri("http://httpbin.org:80")) //redirect to
.route(p -> p.path("/currency-exchange/**") // 추가
.uri("lb://currency-exchange")) // 추가
.build();
}
}
spring.application.name=api-gateway
server.port=8765
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka
// spring.cloud.gateway.discovery.locator.lowerCaseServiceId=true
// spring.cloud.gateway.discovery.locator.enabled=true
// spring.cloud.gateway.discovery.locator.lower-case-service-id=true
이제 Exchange 마이크로서비스를 직접적으로 요청하는
http://localhost:8000/currency-exchange/from/USD/to/INR 포트번호 8000
번이 아닌
8765
번 게이트웨이 http://localhost:8765/currency-exchange/from/USD/to/INR로 요청해도 똑같이 동작
@Configuration
public class ApiGateConfiguration {
@Bean
public RouteLocator gatewayRoute(RouteLocatorBuilder builder) {
return builder.routes()
.route(p -> p.path("/get")
.filters(f -> f
.addRequestHeader("MyHeader", "MyURI")
.addRequestParameter("Param", "MyValue"))
.uri("http://httpbin.org:80")) //redirect to
.route(p -> p.path("/currency-exchange/**")
.uri("lb://currency-exchange"))
.route(p -> p.path("/currency-conversion/**") // 추가
.uri("lb://currency-conversion")) // 추가
.route(p -> p.path("/currency-conversion-feign/**") // 추가
.uri("lb://currency-conversion")) // 추가
.build();
}
}
@Configuration
public class ApiGateConfiguration {
@Bean
public RouteLocator gatewayRoute(RouteLocatorBuilder builder) {
return builder.routes()
.route(p -> p.path("/get")
.filters(f -> f
.addRequestHeader("MyHeader", "MyURI")
.addRequestParameter("Param", "MyValue"))
.uri("http://httpbin.org:80")) //redirect to
.route(p -> p.path("/currency-exchange/**")
.uri("lb://currency-exchange"))
.route(p -> p.path("/currency-conversion/**")
.uri("lb://currency-conversion"))
.route(p -> p.path("/currency-conversion-feign/**")
.uri("lb://currency-conversion"))
.route(p -> p.path("/abc/**") // 추가
.filters(f -> f.rewritePath( // 추가
"/abc/(?<segment>.*)", // 정규식 추가
"/currency-conversion-feign/${segment}")) // 추가
.uri("lb://currency-conversion")) // 추가
.build();
}
}
이제 /abc로 요청하도 /currency-conversion-feign로 rewrite해서 요청하게 된다.
@Component
public class LoggingFilter implements GlobalFilter{
private Logger logger = LoggerFactory.getLogger(LoggerFinder.class);
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
logger.info("Path of the request received -> {}", exchange.getRequest().getPath());
return chain.filter(exchange);
}
}
모든 요청에 대한 인증 같은 걸 구현하고 싶다면
여기가 그걸 구현하기에 알맞은 곳이죠
Spring Cloud Gateway의 중요한 특징은 요청의 속성을 맞출 수 있다는 점이에요.
앞서 경로에 근거해 매치하는 법을 봤죠 게다가 많은 다양한 것들을 기반으로 매치시킬 수 있어요
호스트에 따라 매치할 수 있고
요청 메서드에 따라 매치할 수 있어요
쿼리 매개 변수 기반으로 매치할 수도 있고
여러 매개 변수 기반으로 요청을 매치할 수도 있어요
어떤 요청 속성에서도 라우트를 매치할 수 있고 Predicate와 Filters도 정의할 수 있죠