API 게이트웨이 (Spring Cloud Gateway)

ayboori·2024년 8월 5일
0

Spring

목록 보기
14/24
post-thumbnail

API 게이트웨이란?

참고

  • API 게이트웨이는 클라이언트의 요청을 받아 백엔드 서비스로 라우팅하고, 다양한 부가 기능을 제공하는 중간 서버입니다.
  • 백엔드 서비스에 대한 API 트래픽을 수락, 변환, 라우팅 및 관리하는 중개자 역할을 하는 서비스, 장치 또는 프록시
  • 클라이언트와 서비스 간의 단일 진입점 역할을 하며, 보안, 로깅, 모니터링, 요청 필터링 등을 처리합니다.

API 게이트웨이의 주요 기능

  • 라우팅: 클라이언트 요청을 적절한 서비스로 전달
  • 인증 및 권한 부여: 요청의 인증 및 권한을 검증
  • 로드 밸런싱: 여러 서비스 인스턴스 간의 부하 분산
  • 모니터링 및 로깅: 요청 및 응답을 로깅하고 모니터링
  • 요청 및 응답 변환: 요청과 응답을 변환하거나 필터링

Spring Cloud Gateway란?

  • Spring Cloud Gateway는 Spring 프로젝트의 일환으로 개발된 API 게이트웨이로, 클라이언트 요청을 적절한 서비스로 라우팅하고 다양한 필터링 기능을 제공합니다.
  • Spring Cloud Netflix 패키지의 일부로, MSA에서 널리 사용됩니다.
  • 모든 인입 요청을 처리하고 적절한 MSA로 라우팅 하는 중앙 집중식 처리점을 제공

제공 방식

스크린샷 2024-09-09 오후 3.32.27.png

  • Reactive Gateway → Netty 서버 기반, 비동기식 I/O를 통해 높은 성능 보장
  • Gateway → MVC Servlet 기반 (기존 코드와의 호환 👍🏻 아직 버그가 좀 있음 👎🏻)

Spring Cloud Gateway의 주요 특징

  • 동적 라우팅: 요청의 URL 패턴/ URI, 호스트, HTTP 메소드 등 다양한 조건에 따라 동적으로 APP (백엔드 서비스)으로 라우팅
  • 필터링: 요청 전후에 다양한 작업을 수행할 수 있는 필터 체인 제공
    인증 (Login, Jwt) / 인가 (Role) 확인
  • Predicate : if-else 문과 같이 요청의 특정 속성(헤더, 요청 경로)에 기반하여 경로를 선택하는 로직 ➡️ ant 스타일 패턴 매칭 사용으로 특정 경로의 요청은 특정 게이트웨이
  • 모니터링: 요청 로그 및 메트릭을 통해 서비스 상태 모니터링
  • 보안: 요청의 인증 및 권한 검증
  • 비동기 및 논블로킹 : I/O를 사용하여 고성능 네트워킹 제공 (Reactive Gateway 사용하는 경우)
  • Rate limiting 및 Circuit breaker : 네트워크 트래픽을 조절하고 서비스 간의 의존성 문제를 관리 (필터로 관리 가능)

Spring Cloud Gateway 설정

기본 설정

  • Spring Cloud Gateway를 사용하려면 Spring Boot 애플리케이션에 의존성을 추가해야 합니다.
  • build.gradle 파일 예시:
    
    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-web'
    	  implementation 'org.springframework.boot:spring-boot-starter-actuator'
    	  implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
    	  implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
    }
    

라우팅 설정

  • application.yml 파일에서 라우팅 설정을 정의할 수 있습니다.
  • 예시 설정 파일:
    
    spring:
      cloud:
        gateway:
          discovery:
            locator:
              enabled: true  # 서비스 디스커버리를 통해 동적으로 라우트를 생성하도록 설정
          routes:
            - id: users-service  # 라우트 식별자
              uri: lb://users-service # 'users-service'라는 이름으로 로드 밸런싱된 서비스로 라우팅 (lb : 로드 밸런싱 되어 있음)
              predicates:
                - Path=/users/** # /users/** 경로로 들어오는 요청을 이 라우트로 처리
            - id: orders-service  # 라우트 식별자
              uri: lb://orders-service  # 'orders-service'라는 이름으로 로드 밸런싱된 서비스로 라우팅
              predicates:
                - Path=/orders/** #/orders/** 경로로 들어오는 요청을 이 라우트로 처리
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:8761/eureka/
    

Spring Cloud Gateway 필터링

필터 종류

  • Global Filter: 모든 요청에 대해 작동하는 필터
    - 로깅, 인증

  • Gateway Filter: 특정 라우트에만 적용되는 필터
    - 특정 endpoint에 대해 요청 헤더를 추가, 응답 수정, 로깅 등 세분화 된 로직 구현 가능

필터 구현

  • 필터를 구현하려면 GlobalFilter 또는 GatewayFilter 인터페이스를 구현하고, filter 메서드를 오버라이드해야 합니다.

필터 주요 객체

  • Mono
    • Mono는 리액티브 프로그래밍에서 0 또는 1개의 데이터를 비동기적으로 처리합니다.
    • Mono<Void>는 아무 데이터도 반환하지 않음을 의미합니다.

      비동기 : 동시에 동작이 시작되므로 다음 프로세스로 넘어갈 때 모든 동작의 완료를 체크

  • ServerWebExchange
    	> 필터를 통한 요청이 있을 때 요청과 응답을 가져옴
    • ServerWebExchange는 HTTP 요청과 응답을 캡슐화한 객체입니다.
    • exchange.getRequest()로 HTTP 요청을 가져옵니다.
    • exchange.getResponse()로 HTTP 응답을 가져옵니다.
  • GatewayFilterChain
    • GatewayFilterChain은 여러 필터를 체인처럼 연결합니다.
    • chain.filter(exchange)는 다음 필터로 요청을 전달합니다.

필터 시점별 종류

Pre 필터

Pre 필터는 요청이 처리되기 전 (라우팅 전) 에 실행됩니다. 따라서 Pre 필터에서는 요청을 가로채고 필요한 작업을 수행한 다음, 체인의 다음 필터로 요청을 전달합니다. 이때, 추가적인 비동기 작업을 수행할 필요가 없기 때문에 then 메서드를 사용할 필요가 없습니다.

    
    @Component
    public class PreFilter implements GlobalFilter, Ordered {
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            // 요청 로깅
            System.out.println("Request: " + exchange.getRequest().getPath());
            return chain.filter(exchange);
        }
    
        @Override
        public int getOrder() {  // 필터의 순서를 지정합니다.
            return -1;  // 필터 순서를 가장 높은 우선 순위로 설정합니다.
        }
    }
    

Post 필터

Post 필터는 요청이 처리된 후(서비스에서 처리 후), 응답이 반환되기 전에 실행됩니다. Post 필터에서는 체인의 다음 필터가 완료된 후에 실행되어야 하는 추가적인 작업을 수행해야 합니다. 따라서 chain.filter(exchange)를 호출하여 다음 필터를 실행한 후, then 메서드를 사용하여 응답이 완료된 후에 실행할 작업을 정의합니다.

    
    @Component
    public class PostFilter implements GlobalFilter, Ordered {
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // then 내에 다음 요청 작성하는 것
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                // 응답 로깅
                System.out.println("Response Status: " + exchange.getResponse().getStatusCode());
            }));
        }
    
        @Override
        public int getOrder() {
            return -1;
        }
    }
    

필터의 종류

  • AddRequestHeader
  • AddRequestHeadersIfNotPresent
  • AddRequestParameter
  • AddResponseHeader
  • PrefixPath
  • ModifyResponseBody
  • RequestRateLimiter
  • 등등..

yml에 적어서 적용할 수 있다.

코드를 응용하고 custom하여 원하는 기능을 구현할 수 있다.

설정

Gateway 설정은 두 가지 방법으로 가능합니다.

  • application.yml or application.properties에 YAML 형식으로 설정 → 설정파일을 통해 routing 관리! 코드수정 없이 설정 변경
  • Java 코드로 설정 → 더 높은 유연성 제공!

id : 라우팅 룰
url : Spring의 서비스 네임 (요청을 보낼 서비스)
predicates : 라우팅 룰을 적용할 경로
filter : 필터링 할 동작


🤔 더 알고싶어요

오늘 배운 내용을 어떻게 응용 할 수 있나요? (자유선택 숙제 🤗)

  • 고가용성 시스템을 구축해보세요! → Eureka 서버를 두 대 이상 만들어야 한다.
  • CH4. 프로젝트에서 Gateway + Spring Security + JWT 인증 구현을 해보세요! → hint GlobalFilter
  • CH4. 프로젝트에서 Gateway Filter를 적용해보세요! → 참고 블로그 여기에서 필요한 filter를 적용해서 좀 더 고도화된 API 구현을 해보세요!
  • 게이트웨이단에서 요청의 권한을 확인하는 로직이나 요청이 리소스 소유자임을 확인하는 로직을 customFilter로 구현할 수도 있다!
  • CH4. 프로젝트에서 특정 route의 로그를 개편해보세요! → hint 로그를 수집해보는것도 좋을 것 같은데…🤔 > Filter 참고

Spring Cloud와의 통합

Spring Cloud와의 통합

  • Spring Cloud Gateway는 Spring Cloud Netflix 패키지의 일부로, Eureka와 쉽게 통합할 수 있습니다.
  • Eureka를 통해 동적으로 서비스 인스턴스를 조회하여 로드 밸런싱과 라우팅을 수행할 수 있습니다.

실습

클라우드 게이트웨이 + 유레카 + Order 인스턴스(1개) + Product 인스턴스(2개) 로 진행해봅니다.

Eureka 및 Order, Product

  • 로드밸런싱” 실습에서의 유레카 서버와 Order, Product를 그대로 복사하여 가져옵니다.

게이트 웨이

dependencies
Reactive Gateway, Spring Boot Actuator, Eureka Discovery Client, Lombok, Spring Web 추가

  • CustomPreFilter.java
    implements GlobalFilter, Ordered 하고, 필수 구현해야 하는 메소드 구현
    Mono<Void>는 아무 데이터도 반환하지 않음을 의미합니다.
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.core.Ordered;
    import org.springframework.http.server.reactive.ServerHttpRequest;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;
    
    import java.util.logging.Logger;
    
    @Component
    public class CustomPreFilter implements GlobalFilter, Ordered {
    
        private static final Logger logger = Logger.getLogger(CustomPreFilter.class.getName());
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        	// 서버의 요청을 가져온다.
            ServerHttpRequest response = exchange.getRequest();
            logger.info("Pre Filter: Request URI is " + response.getURI());
            // URI 로그에 찍기
    
            return chain.filter(exchange);
        }
    
        @Override
        public int getOrder() {
        	// 가장 먼저 실행됨
            return Ordered.HIGHEST_PRECEDENCE;
        }
    }
    
  • CustomPostFilter.java
    implements GlobalFilter, Ordered 하고, 필수 구현해야 하는 메소드 구현
    import org.springframework.cloud.gateway.filter.GlobalFilter;
    import org.springframework.core.Ordered;
    import org.springframework.http.server.reactive.ServerHttpResponse;
    import org.springframework.stereotype.Component;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Mono;
    
    import java.util.logging.Logger;
    
    @Component
    public class CustomPostFilter implements GlobalFilter, Ordered {
    
        private static final Logger logger = Logger.getLogger(CustomPostFilter.class.getName());
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, org.springframework.cloud.gateway.filter.GatewayFilterChain chain) {
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            	// 서버의 응답 가져옴 (Reactive에서 import 해야 한다)
                ServerHttpResponse response = exchange.getResponse();
                logger.info("Post Filter: Response status code is " + response.getStatusCode());
            }));
        }
    
        @Override
        public int getOrder() {
			// 가장 나중에 실행됨
            return Ordered.LOWEST_PRECEDENCE;
        }
    }
  • application.yml
    server:
      port: 19091  # 게이트웨이 서비스가 실행될 포트 번호
    
    spring:
      main:
        web-application-type: reactive  # Spring 애플리케이션이 리액티브 웹 애플리케이션으로 설정됨
      application:
        name: gateway-service  # 애플리케이션 이름을 'gateway-service'로 설정
      cloud:
        gateway:
          routes:  # Spring Cloud Gateway의 라우팅 설정
            - id: order-service  # 라우트 식별자
              uri: lb://order-service  # 'order-service'라는 이름으로 로드 밸런싱된 서비스로 라우팅
              predicates:
                - Path=/order/**  # /order/** 경로로 들어오는 요청을 이 라우트로 처리
            - id: product-service  # 라우트 식별자
              uri: lb://product-service  # 'product-service'라는 이름으로 로드 밸런싱된 서비스로 라우팅
              predicates:
                - Path=/product/**  # /product/** 경로로 들어오는 요청을 이 라우트로 처리
          discovery:
            locator:
              enabled: true  # 서비스 디스커버리를 통해 동적으로 라우트를 생성하도록 설정
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:19090/eureka/  # Eureka 서버의 URL을 지정

6.7.5 Run

  • 유레카 서버 ⇒ 게이트웨이 ⇒ 주문 ⇒ 상품 순으로 어플리케이션을 실행합니다.
  • http://localhost:19090에 접속하여 각 인스턴스를 확인합니다.
  • http://localhost:19091/order 로 접속하여 게이트웨이에서 order 서비스를 호출하는 것을 확인 할 수 있습니다.
  • http://localhost:19091/product 를 여러번 호출 하면서 포트가 달라지는 것을 확인합니다. 이를통해 로드밸런싱이 동작함을 확인합니다.
  • 게이트웨이의 로그를 보면 호출 할때마다 필터가 동작하는것을 확인 할 수 있습니다.
profile
프로 개발자가 되기 위해 뚜벅뚜벅.. 뚜벅초

0개의 댓글