API Gateway Service
- 인증 및 권한 부여
- 서비스 검색 통합
- 응답 캐싱
- 정책, 회로 차단기 및 Qos
- 속도 제한
- 부하 분산
- 로깅, 추적, 상관관계
- 헤더, 쿼리 문자열 및 청구 변환
- IP 허용 목록에 추가
SpringCloud에서의 MSA간 통신
- RestTemplate
RestTemplate restTemplate = new RestTemplate();
restTemplate.getForObject("http://localhost:8080/", User.class, 200);
- Feign Client
- FeignClient라는 마이크로서비스 이름가지고 호출이 가능함.
@FeignClient("stores")
public interface StoreClient {
@RequestMapping(method = RequestMethod.GET, value="/stores")
List<Store> getStores();
}
Ribbon
- Client side Load Balancer
- SpringBoot 2.4에서 Maintenance 상태임
- 서비스 이름으로 호출
- Health Check
Netflix Zuul
1. 테스트용 프로젝트 생성하기
FirstService 프로젝트
package com.example.firstserivce;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/")
public class FirstServiceController {
@GetMapping("/welcome")
public String welcome() {
return "Welcome to the First Service";
}
}
application.yml
server:
port: 8081
spring:
application:
name: my-first-service
eureka:
client:
fetch-registry: false
register-with-eureka: false
SecondService
package com.example.secondservice;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/")
public class SecondServiceController {
@GetMapping("/welcome")
public String welcome() {
return "Welcome to the Second Service";
}
}
application.yml
server:
port: 8082
spring:
application:
name: my-second-service
eureka:
client:
fetch-registry: false
register-with-eureka: false
Zuul 프로젝트 생성
SpringCloundRouting - Zuul을 사용하려면 스프링부트 2.4이하 버전을 사용해야한다.
@EnableZuulProxy를 사용한다.
@SpringBootApplication
@EnableZuulProxy
public class ZuulServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulServiceApplication.class, args);
}
}
server:
port: 8000
spring:
application:
name: my-zuul-service
zuul:
routes:
first-service:
path: /first-service/**
url: http://localhost:8081
second-service:
path: /second-service/**
url: http://localhost:8082
같은 url로 서비스를 라우팅 해준다.
요청이 들어오기전에 filter를 구현하여 Log출력을 해볼 수도 있다!
package com.example.zuulservice.filter;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.netflix.zuul.exception.ZuulException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
@Slf4j // Logger객체를 바로 사용할 수 있다.
@Component
public class ZuulLoggingFilter extends ZuulFilter {
@Override
public String filterType() {
return "pre";
}
@Override
public int filterOrder() {
return 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() throws ZuulException {
log.info("*********************** printing logs: ");
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
log.info("*********************** : " + request.getRequestURI());
return null;
}
}
Spring Cloud Gateway
비동기 처리가 가능하다.
해당 gateway를 어디로 routing시켜줄지 설정시켜줄 수 있다.
1. Client 요청이 오면 header추가
2. Predicate
3. Pre Filter
4. Post Filter
5. Client한테 response할때 header값 추가함
= > header값 추가하는 작업을 SpringCloud가 대신해준다.
server:
port: 8011
eureka:
client:
register-with-eureka: false
fetch-registry: true
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/**
- id: second-service
uri: http://localhost:8081
predicates:
- Path=/second-service/**
application.yml에 저장해두었던 부분을 java code로도 작성할 수 있다! Lambda를 이용해서!
Filter/FilterConfig.java
앞의 addRequestHeader, addResponseHeader를 통해서 header를 추가할 수 있다.
package com.example.apigatewayservice.config;
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();
}
}
FirstService 프로젝트에 FirstServiceController.java에 해당 message 엔드포인트 추가한다
@GetMapping("/message")
public String message(@RequestHeader("first-request") String header) {
log.info(header);
return "Welcome to the First Service";
}
SecondService프로젝트에도 추가함
@GetMapping("/message")
public String message(@RequestHeader("second-request") String header) {
log.info(header);
return "Welcome to the Second Service";
}
application.yml 파일에 filter를 추가할 수도 있다.
spring:
application:
name: apigateway-service
cloud:
gateway:
routes:
- id: first-service
uri: http://localhost:8081 # 이동될 주소
predicates:
- Path=/first-service/**
filters:
- AddRequestHeader=first-request, first-requests-header2
- AddResponseHeader=first-response, first-response-header2
- id: second-service
uri: http://localhost:8081
predicates:
- Path=/second-service/**
filters:
- AddRequestHeader=second-request, second-requests-header2
- AddResponseHeader=second-response, second-response-header2
Postman으로 추가하면 header로 해당 정보를 확인할 수 있다.
CustomFilter
Custom Filter.java
package com.example.apigatewayservice.filter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
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) {
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: response code -> {}", response.getStatusCode());
}));
});
}
public static class Config {
}
}
GlobalFilter
- 공통으로 다 쓸 수 있는 filter
spring:
application:
name: apigateway-service
cloud:
gateway:
default-filters:
- name:
args:
baseMessage: SpringCloud Gateway Global Filter
preLogger: true
postLogger: true
package com.example.apigatewayservice.filter;
import lombok.Data;
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.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
@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());
}
// Custom Post Filter
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
if(config.isPostLogger()) {
log.info("Global Filter end: request id -> {}", response.getStatusCode());
}
}));
});
}
@Data
public static class Config {
private String baseMessage;
private boolean preLogger;
private boolean postLogger;
}
}
LoggingFilter
package com.example.apigatewayservice.filter;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.OrderedGatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
@Component
@Slf4j
public class LoggingFilter extends AbstractGatewayFilterFactory<LoggingFilter.Config> {
public LoggingFilter() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
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 code -> {}", response.getStatusCode());
}
}));
}, Ordered.LOWEST_PRECEDENCE);
return filter;
}
@Data
public static class Config {
private String baseMessage;
private boolean preLogger;
private boolean postLogger;
}
}
application.yml
spring:
application:
name: apigateway-service
cloud:
gateway:
default-filters:
- name:
args:
baseMessage: SpringCloud Gateway Global Filter
preLogger: true
postLogger: true
routes:
- id: first-service
uri: lb://MY-FIRST-SERVICE
predicates:
- Path=/first-service/**
filters:
- AddRequestHeader=first-request, first-request-header2
- AddResponseHeader=first-response, first-response-header2
# - CustomFilter
- id: second-service
uri: lb://MY-SECOND-SERVICE
predicates:
- Path=/second-service/**
filters:
- AddRequestHeader=second-request, second-request-header2
- AddResponseHeader=second-response, second-response-header2
# - name: CustomFilter
- name: LoggingFilter
args:
baseMessage: Hi, there.
preLogger: true
postLogger: true
Eureka 연동
Eureka에 위치가 어디 저장되었는지 물어보고 -> forwarding 해준다.
Eureka 서버를 실행시켜주고, http://localhost:8761 접속하면 기동이 된다.
'Server🧤 > SpringCloud' 카테고리의 다른 글
[MSA] - 섹션1. Service Discovery (0) | 2023.09.20 |
---|---|
[MSA] MicroService와 SpringCloud의 소개 (0) | 2023.09.13 |