Section 2: API Gateway Service - KwangtaekJung/MSA-SpringCloud-user-service GitHub Wiki
Section 2: API Gateway Service
API Gateway Service ๊ฐ์
Netflix Ribbon๊ณผ Zuul
Spring Cloud Gateway - ๊ธฐ๋ณธ
Spring Cloud Gateway - Filter
Spring Cloud Gateway - Eureka ์ฐ๋
Spring Cloud Gateway - Load Balancer
API Gateway Service ๊ฐ์
- ์ธ์ฆ ๋ฐ ๊ถํ ๋ถ์ฌ
- ์๋น์ค ๊ฒ์ ํตํฉ
- ์๋ต ์บ์ฑ
- ์ ์ฑ , ํ๋ก ์ฐจ๋จ๊ธฐ ๋ฐ QoS ๋ค์ ์๋
- ์๋ ์ ํ
- ๋ถํ ๋ถ์ฐ
- ๋ก๊น , ์ถ์ , ์๊ด ๊ด๊ณ
- ํค๋, ์ฟผ๋ฆฌ ๋ฌธ์์ด ๋ฐ ์ฒญ๊ตฌ ๋ณํ
- IP ํ์ฉ ๋ชฉ๋ก์ ์ถ๊ฐ
Netflix Zuul ๊ตฌํ
- Spring Boot: 2.4.0 ์ดํ ์์ ์ง์ํจ. (์ต์ ๋ฒ์ ์ Spring Cloud Routing์์๋ Zuul ์ง์ํ์ง ์์.)
- First Service, Second Service
- Spring Boot: 2.3.8
- Developer Tools: Lombok
- Web: Spring Web
- Zuul Service
- Spring Boot: 2.3.8
- Web: Spring Web
- Spring Cloud Routing: Zuul (API Gateway ์ญํ )
@EnableZuulProxy
- ZuulFilter
Spring Cloud Gateway - ๊ธฐ๋ณธ
- Developer Tools: Spring Boot DevTools
- Spring Cloud Discovery: Eureka Discovery Client
- Spring Cloud Routing: Gateway
Spring Cloud Gateway - Filter
- Filter using Java Code - FilterConfig.java
@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(); } }
- Filter using with application.yml
- Configuration java ์ฝ๋๋ฅผ ์์ฑํ์ง ์๊ณ applicaiton.yml ์ค์ ๋ง์ผ๋ก๋ ๊ฐ๋ฅํจ.
spring:
application:
name: apigateway-service
cloud:
gateway:
routes:
- id: first-serivce
uri: http://localhost:8081
predicates:
- Path=/first-service/**
filters:
- AddRequestHeader=first-request, first-request-header2
- AddResponseHeader=first-response, first-response-header2
- id: second-serivce
uri: http://localhost:8082
predicates:
- Path=/second-service/**
filters:
- AddRequestHeader=second-request, second-request-header2
- AddResponseHeader=second-response, second-response-header2
- Custom Filter
- ์ฌ์ฉ์ ํํฐ๋ฅผ ๊ตฌํํ๊ณ application.yml์์ ํํฐ๊ฐ ํ์ํ routes ์ ์ค์ ํ๋ค.
@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
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("Custom POST filter: request id -> {}", response.getStatusCode());
}));
});
}
public static class Config {
}
}
spring:
application:
name: apigateway-service
cloud:
gateway:
routes:
- id: first-serivce
uri: http://localhost:8081
predicates:
- Path=/first-service/**
filters:
# - AddRequestHeader=first-request, first-request-header2
# - AddResponseHeader=first-response, first-response-header2
- CustomFilter
- id: second-serivce
uri: http://localhost:8082
predicates:
- Path=/second-service/**
filters:
# - AddRequestHeader=second-request, second-request-header2
# - AddResponseHeader=second-response, second-response-header2
- CustomFilter
- Global Filter
- Global Filter๋ฅผ ๊ตฌํํ๊ณ applicatin.yml์ default-filters๋ฅผ ์ค์ ํ๋ค.
- Logging Filter
- ์ฐจ์ด์ ์ ๋ชจ๋ฅด๊ฒ ๊ตฐ. ใ กใ ก;
@Component
@Slf4j
public class LoggingFilter extends AbstractGatewayFilterFactory<LoggingFilter.Config> {
public LoggingFilter() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
// return ((exchange, chain) -> {
// ServerHttpRequest request = exchange.getRequest();
// ServerHttpResponse response = exchange.getResponse();
//
// log.info("Global 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 Filter End: response id -> {}", response.getStatusCode());
// }
// }));
// });
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 Start: request id -> {}", request.getId());
}
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
if (config.isPostLogger()) {
log.info("Logging POST End: response id -> {}", response.getStatusCode());
}
}));
}, Ordered.HIGHEST_PRECEDENCE);
return filter;
}
@Data
public static class Config {
private String baseMessage;
private boolean preLogger;
private boolean postLogger;
}
}
Spring Cloud Gateway - Eureka ์ฐ๋
- Eureka Server: discovery service
- Eureka Client: APIGATEWAY-SERVICE, MY-FIRST-SERVICE, MY-SECOND-SERVICE
Spring Cloud Gateway - Load Balancer
- uri์ port๋ฅผ ๋ช ์ํ์ง ์๊ณ Load Balancer๋ฅผ ํตํ๋๋ก ํ๋ค.
routes:
- id: first-serivce
# uri: http://localhost:8081
uri: lb://MY-FIRST-SERVICE
- First Service, Second Service๋ฅผ ๊ฐ 2๊ฐ์ฉ ๊ธฐ๋
- ๊ถ๊ทน์ ์ผ๋ก Random Port๋ฅผ ์ด์ฉํ๋ ๋ฐฉ์์ผ๋ก Load Balancer๋ฅผ ํตํ๋๋ก ํ๋ค. ๋ฐฉ๋ฒ1,2,3)์ ์ฐธ๊ณ ๋ง ํ์.
- ๋ฐฉ๋ฒ1) VM Options -> -Dserver.port=[๋ค๋ฅธํฌํธ]
- ๋ฐฉ๋ฒ2)
$ mvn spring-boot:run -Dspring-boot.run.jvmArguments='-Dserver.port=9003'
- ๋ฐฉ๋ฒ3)
$ mvn clean compile package $ java -jar -Dserver.port=9004 ./target/user-service-0.0.1-SNAPSHOT.jar
- ๋ฐฉ๋ฒ4) Random Port ์ค์ ํ๋ค. ์ฒซ๋ฒ์งธ๋ Intellij์์ Run, ๋๋ฒ์งธ๋ Terminal์์ ํฌํธ ์ง์ ์์ด application ๊ตฌ๋
$ mvn spring-boot:run
server:
port: 0 #random port
spring:
application:
name: my-first-service
eureka:
client:
register-with-eureka: true
fetch-registry: true
service-url:
defaultZone: http://localhost:8761/eureka
instance: #๋๋คํฌํธ ์ค์ ์ Eureka Server์์ port๊ฐ 0์ผ๋ก ๋ชจ๋ ํ์๋๋ฏ๋ก ๊ฐ๊ฐ์ ๊ตฌ๋ณํ๊ธฐ ์ํด ๋ค์์ฒ๋ผ ์ค์ ํ๋ค.
instance-id: ${spring.application.name}:${spring.application.instance_id:${random.value}}