Dev/Java

Spring Cloud Gateway란?

두넌 2024. 2. 22.

프로젝트를 진행하다, 서버가의 개수가 늘어남에 따라 각 서버에 대한 정보를 관리하는 데에 어려움이 생겨 Gateway를 도입하기로 하였다

관련된 정보를 공부하면서, 얻은 지식들을 정리해 보고자 한다

 

1 - API Gateway

Client — API Gateway — Backend Service

→ 클라이언트와 백엔드 서비스 사이에 위치하는 리버스 프록시 역할을 하는 서비스

리버스 프록시란 뭘까?

그 전에 프록시란 뭘까?

1.1 - proxy

클라이언트가 자신을 통해서 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해주는 시스템, 응용 프로그램

  • 보안성, 성능, 안정성 향상
  • forward proxy, reverse proxy

https://research.aimultiple.com/forward-vs-reverse-proxy/

Forward Proxy

우리가 흔히 말하는 프록시 서버

  • 클라이언트가 인터넷 요청을 보내면, 프록시 서버가 가로채고 다시 프록시 서버가 해당 요청을 웹서버에 보냄
  • 프록시 서버는 웹 서버로부터 받은 응답을 다시 클라이언트에게 전달
  • 학교에서 게임 사이트나 불법 사이트 접근 못하게 막는 것
    • 기관에 속한 유저가 특정 컨텐츠에 접근하는 것을 방지
  • 익명성을 보장

Reverse Proxy

웹서버 앞에 프록시 서버가 놓여 있음

  • 로드 밸런싱(Load balancing)
    • 대량의 트래픽을 하나의 싱글 서버로 감당하기 어려우므로, 특정 서버가 과부화 되지 않게 로드 밸런싱
    • 보안
      • 서버의 본래 IP주소를 노출시킬 필요 없음
    • 캐싱, SSL 암호

 

MSA(Micro Service Architecture)

  • 소프트웨어 시스템을 여러 작은 독립적인 서비스로 분할, 개발, 배포

1.2 - 목적

하나의 시스템을 마이크로서비스화(coupon, api, auth …)함에 따라 발생하는 트래픽, API 숫자 증가 → 전체적인 API 관리 포인트의 필요성

2 - Spring Cloud Gateway

2.1 - 동작

  1. 클라이언트가 Spring Cloud Gateway로 요청
  2. Gateway Handler Mapping이 Route의 조건에 일치하는 요청이라고 판단
    1. Gateway Web Handler가 요청에 관련된 필터들을 통해 요청을 보냄
    2. Filter는 요청의 기능에 따른 로직을 수행

2.2 - 결론

우리가 ckin-gateway 의 IP address, Port number만 알고 있으면

front 에서 각 api(coupon, api, auth)로 요청을 보낼 때, endpoint만 바꿔서 보내면 된다

2.3 - 구성요소

 

Route

고유 ID, 목적지 URI, Predicate, Filter로 구성된 구성 요소

  • Gateway로 요청된 URI 조건이 참인 경우 매핑된 해당 URI로 매칭
r -> r.path("/auth/**")
// "/auth" 하위로 들어오는 모든 요청의 경우

.uri("http://address:port")
// 매핑된 해당 URI로 매칭

Predicate

주어진 요청이 주어진 조건을 충족하는 지 테스트하는 구성 요소

만약 매칭되는 Predicate가 하나도 없다면 404 Not Found 를 응답

Filter

들어오는 요청, 나가는 응답에 대하여 필요한 로직 작성

2.4 - 코드

@Configuration
@RequiredArgsConstructor
public class RouteConfig {
    private final ServerInfoProperties serverInfoProperties;
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
        return routeLocatorBuilder.routes()
                .route("ckin-auth", r -> r.path("/auth/**")
                        .uri(serverInfoProperties.getAuthUri()))
                .route("ckin-coupon", r -> r.path("/coupon/**")
                        .uri(serverInfoProperties.getCouponUri()))
                .route("ckin-api", r -> r.path("/api/**")
                        .uri(serverInfoProperties.getApiUri()))
                .build();
    }
}

application.properties의 경우

spring.cloud.gateway.routes[0].id=pgService
spring.cloud.gateway.routes[0].uri=http://localhost:2005/
spring.cloud.gateway.routes[0].predicates[0]=Path=/employee/**
spring.cloud.gateway.routes[1].id=inMateService
spring.cloud.gateway.routes[1].uri=http://localhost:2006/
spring.cloud.gateway.routes[1].predicates[0]=Path=/consumer/**

2.5 - 확장

3 - 동작

API 서버로의 직접 요청

GET http://localhost:7030/api/tag
Content-Type: application/json

→ 응답

HTTP/1.1 200 
Content-Type: application/json
Transfer-Encoding: chunked
Date: Fri, 16 Feb 2024 05:31:51 GMT
Keep-Alive: timeout=60
Connection: keep-alive

[
  {
    "tagId": 1,
    "tagName": "hello"
  },
  {
    "tagId": 2,
    "tagName": "hello"
  },
  {
    "tagId": 3,
    "tagName": "hello"
  },
  {
    "tagId": 4,
    "tagName": "hello"
  },
  {
    "tagId": 5,
    "tagName": "hello"
  }
]

Gateway 서버로의 요청

GET http://localhost:9010/api/tag
Content-Type: application/json

→ 응답

HTTP/1.1 200 
Content-Type: application/json
Transfer-Encoding: chunked
Date: Fri, 16 Feb 2024 05:31:51 GMT
Keep-Alive: timeout=60
Connection: keep-alive

[
  {
    "tagId": 1,
    "tagName": "hello"
  },
  {
    "tagId": 2,
    "tagName": "hello"
  },
  {
    "tagId": 3,
    "tagName": "hello"
  },
  {
    "tagId": 4,
    "tagName": "hello"
  },
  {
    "tagId": 5,
    "tagName": "hello"
  }
]

 

Reference


https://metanetglobal.com/bbs/board.php?bo_table=tech&wr_id=38

https://losskatsu.github.io/it-infra/reverse-proxy/#3-리버스-프록시reverse-proxy-서버란

댓글