API Gateway Service - Spring Cloud Gateway

Jifrozen·2021년 5월 25일
0

msa

목록 보기
2/2

API Gateway Service


사용자가 설정한 라우팅 설정에 따라서 각각 엔드포인트로 클라이언트 대신해서 요청하고 응답 받으면 다시 클라이언트에게 전달하는 프록시 역할

시스템 내부구조 숨기고 외부 요청에 적절한 형태로 응답할 수 있다.

클라이언트측에서 단일 진입로가 필요했기 때문에 그 역할을 API Gateway가 수행

  • 인증 및 권한 부여
  • 서비스 검색 통합
  • 응답캐싱 저장
  • 일괄적 정책, 문제 생길때 회로 차단기 기능
  • 속도제한
  • 부하 분산처리 - 3개의 인스턴스가 나눠 작업을 할 경우 클라이언트 A 서비스 필요하면 어디로 갈지 판단해주는 역할
  • 많은 서비스로 구성 연관으로 호출하는 경우가 많음 누구에의해 호출 진입점 그 다음 단계와 같은 추척 관계
  • 각 서비스가 하나의 솔루션을 이용해 로그를 모아줌 로그 파일 단일화 처리
  • 헤더 쿼리 문자열 및 청구 변환
  • 허용 IP 차단 Ip 판단 일종 방화벽 프록시 역할 Gateway통해 들어온 정보만 처리
  1. RestTemplate

  2. Feign Client

Netfix Ribbon


Client side Load Balancer → 비동기 호환 잘 안되는 문제점

  • 서비스 이름으로 호출 (ip:port가 아닌 이름으로 호출)
  • Health Check
  • enspring Boot 2.4에서 Maintenence 상태

Netflix Zuul


  • spring Boot 2.4에서 Maintenence 상태

구현


요청이 들어왔을때 각 서비스에 알맞게 분산되는지 알아보자

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;
    }

}

Spring Cloud Gateway


비동기 처리가 가능하다는 큰 장점을 가짐

Filter



predicate 사전 조건

  1. property
  2. java code


우선순위는 order인자값을 통해 변경 가능하다.

구현


header값을 가져오는 filter

  1. property

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/**
  1. java code
@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();
    }
}

Custom Filter


원하는 라우터 정보에 개별적 등록

구현


  1. 로그인 후 토큰을 받고 계속 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
  1. Logging
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;

Global 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와 연동


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}}

랜덤 포트 지정

0개의 댓글