[Spring Cloud] Spring Cloud Gateway

Wonjun Seo·2024년 3월 17일
0

Spring Cloud Gateway

Spring을 기반으로 API를 구축하는 데 사용되는 Gateway이며 클라이언트와 서비스 사이에 통신을 관리하는 역할을 한다.

Spring Boot 버전 2.4 이상에서는 Netflix Zuul을 더 이상 사용할 수 없어 이를 대체하기 위해 사용한다.

Spring Cloud Gateway는 크게 Route, Predicate, Filter로 구성되어 있다.


Spring Cloud Gateway 구성

Route

Route는 응답을 보낼 목적지 uri와 필터 항목을 식별하기 위한 ID로 구성되어 있고 라우팅 목적지를 의미한다. 요청된 uri의 조건이 predicate와 일치하는지 확인 후, 일치하는 경우 해당 uri 경로로 요청을 매칭 시켜준다.

Predicate

API Gateway로 들어온 요청이 주어진 조건을 만족하는지 확인하는 역할을 한다.
하나 이상의 조건을 정의할 수 있으며, 들어온 HTTP 요청이 predicate 조건에 맞지 않는 경우 HTTP 404 코드를 반환한다.

Filter

API Gateway로 들어오는 요청에 대해 Filter를 적용하여 선처리 및 후처리를 할 수 있게 해준다.


Spring Cloud Gateway 작동 원리

클라이언트에서 들어온 요청이 Gateway Handler Mapping을 통해 요청 경로와 일치한지 확인하고, Gateway Web Handler에서 요청과 관련된 필터 체인을 통해 요청이 전송된다.
이후 적용되는 필터를 통해 요청 또는 응답에 필요한 전처리, 후처리를 하고, Proxy Filter는 프록시 요청이 처리될 때 수행된다.


MSA 구조에서의 Spring Cloud Gateway

아래는 Eureka만 구현된 서비스의 구조이다.

여기에 API Gateway가 추가되면 아래와 같은 구조가 된다.

이를 바탕으로한 클라이언트 요청 처리는 다음과 같다.

  1. 각 인스턴스(서비스)를 실행하여 유레카 서버에 등록한다.
  2. 클라이언트에서 요청이 API Gateway로 들어온다.
  3. 게이트웨이에 들어온 요청을 어떤 서비스에서 해결할 지 유레카에게 찾는다.
  4. 유레카가 응답을 전달한다.
  5. 게이트웨이는 받았던 요청을 적절한 서비스에게 전달한다.
  6. 최종 결과를 클라이언트에게 전달한다.

Spring Cloud Gateway 구현

application.yml

server:
  port: 12500

spring:
  application:
    name: API-GATEWAY

  cloud:
    gateway:
      routes:
        - id: api
          uri: http://localhost:8086
          predicates:
            - Path=/api/**
          filters:
            - CustomFilter
      default-filters:
        - name: GlobalFilter
          args:
            baseMessage: Spring Cloud Gateway Global Filter
            preLogger: true
            postLogger: true

eureka:
  instance:
    prefer-ip-address: true
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://localhost:12300/eureka/

application.yml에 Eureka 서버와 API 서비스와 관련된 설정을 추가한다.

Custom Filter

@Component
public class CustomFilter extends AbstractGatewayFilterFactory<CustomFilter.Config> {

    public CustomFilter() {
        super(Config.class);
    }

    public static class Config {}

    @Override
    public GatewayFilter apply(Config config) {
        // Pre Filter
        return ((exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            ServerHttpResponse response = exchange.getResponse();

            System.out.println("Pre Filter : request id -> " + request.getId());

            // Post Filter
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                System.out.println("Post Filter : response code -> " + response.getStatusCode());
            }));
        });
    }
}

Global Filter

@Component
public class GlobalFilter extends AbstractGatewayFilterFactory<GlobalFilter.Config> {

    public GlobalFilter() {
        super(Config.class);
    }

    @Data
    public static class Config{
        private String baseMessage;
        private boolean preLogger;
        private boolean postLogger;
    }

    @Override
    public GatewayFilter apply(GlobalFilter.Config config) {
        // Pre Filter
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();
            ServerHttpResponse response = exchange.getResponse();

            System.out.println("Global Pre baseMessage = " + config.getBaseMessage());

            if(config.isPreLogger()) {
                System.out.println("Global Pre Start : request id -> " + request.getId());
            }

            // Post Filter
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                if(config.isPreLogger()) {
                    System.out.println("Filter End : response code -> " + response.getStatusCode());
                }
            }));
        };
    }
}

0개의 댓글