사용자가 설정한 라우팅 설정에 따라서 각각 엔드포인트로 클라이언트 대신해서 요청하고 응답 받으면 다시 클라이언트에게 전달하는 프록시 역할
시스템 내부구조 숨기고 외부 요청에 적절한 형태로 응답할 수 있다.
클라이언트측에서 단일 진입로가 필요했기 때문에 그 역할을 API Gateway가 수행
RestTemplate
Feign Client
Client side Load Balancer → 비동기 호환 잘 안되는 문제점
요청이 들어왔을때 각 서비스에 알맞게 분산되는지 알아보자
First Service, Second Service, Netflix Zuul
server:
port: 8000
spring:
application:
name: my-suul-service
zuul:
routes:
first-service:
path: /first-service/**
url: http://localhost:8081
second-service:
path: /second-service/**
url: http://localhost:8082
ZuulFilter
@Slf4j
@Component
public class ZuulLoggingFilter extends ZuulFilter{
@Override
public Object run() throws ZuulException {
log.info("******************* pringting logs: ");
RequestContext ctx=RequestContext.getCurrentContext();
HttpServletRequest request=ctx.getRequest();
log.info("******************* "+request.getRequestURI());
return null;
}
@Override
public String filterType() {//사전 사후 필터를 정하는
return "pre";
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
}
비동기 처리가 가능하다는 큰 장점을 가짐
predicate 사전 조건
우선순위는 order인자값을 통해 변경 가능하다.
header값을 가져오는 filter
server:
port: 8000
eureka:
client:
fetch-registry: false
register-with-eureka: 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:
- AddRequestHeader=first-request, first-request-header2
- AddResponseHeader=first-response, first-response-header2
- id: second-service
uri: http://localhost:8082/
filters:
- AddRequestHeader=second-request, second-request-header2
- AddResponseHeader=second-response, second-response-header2
predicates:
- Path=/second-service/**
@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();
}
}
원하는 라우터 정보에 개별적 등록
로그인 후 토큰을 받고 계속 json형태를로 가지고있는 (jwt) 정상적인 토큰을 가지고 있는지 계속 확인하는 체크
비동기 → ServerHttpRequest ServerHttpResponse 이용
@Component
**@Slf4j
public class CustomFilter exten**ds AbstractGatewayFilterFactory<CustomFilter.Config> {
public CustomFilter(){
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
return ((exchange, chain) -> {
ServerHttpRequest request=exchange.getRequest();
ServerHttpResponse response=exchange.getResponse();
log.info("Custom PRE filter : request id -> {}",request.getId());
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("Custom POST filter : response id -> {}",response.getStatusCode());
}));
});
}
public static class Config{
}
}
application.yml
filters:
# - AddRequestHeader=first-request, first-request-header2
# - AddResponseHeader=first-response, first-response-header2
- CustomFilter
GatewayFilter filter=new OrderedGatewayFilter((exchange,chain) -> {
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());
}
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
if (config.isPostLogger()) {
log.info("Logging POST filter : response id -> {}", response.getStatusCode());
}
}));
}, Ordered.HIGHEST_PRECEDENCE);
return filter;
gateway:
default-filters:
- name: GlobalFilter
args:
baseMessage: Spring Cloud Gateway Global Filter
preLogger: true
postLogger: true
routes:
@Component
@Slf4j
public class GlobalFilter extends AbstractGatewayFilterFactory<GlobalFilter.Config> {
public GlobalFilter(){
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
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());
}
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
if (config.isPostLogger()) {
log.info("Global POST filter : response id -> {}", response.getStatusCode());
}
}));
});
}
@Data
public static class Config{
private String baseMessage;
private boolean preLogger;
private boolean postLogger;
}
}
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://localhost:8761/eureka
# routes의 uri값이 가리키는 서비스 이름
routes:
- id: first-service
uri: lb://MY-FIRST-SERVICE
같은 서비스를 2개 이상 기동한다면...
server:
port: 0
spring:
application:
name: my-first-service
eureka:
client:
fetch-registry: true
register-with-eureka: true
service-url:
defaultZone: http://localhost:8761/eureka
instance:
instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}
랜덤 포트 지정