[Spring Cloud Gateway] 프로젝트 생성 및 라우팅 기능

2021. 1. 27. 00:37SPRING

[Spring Cloud Gateway] Zuul 대신 spring cloud gateway

[Spring Cloud Gateway] 프로젝트 생성 및 라우팅 기능

 

 

 

지난번에 이어서 Spring Cloud Gateway 프로젝트 생성부터 간단한 라우팅 기능 추가까지를 포스팅 해보려고 합니다.

 

 

Gradle Dependency 추가

build.gradle에 아래와 같이 추가해 주도록 한다.

- maven 레포지토리 repo.spring.io/milestone 을 추가

- 스프링 클라우드 버전에 대한 변수 지정

- 스프링 클라우드 게이트웨이 dependency 추가

- 스프링 클라우드 의존성 관리를 위한 mavenBom 추가

repositories {
    mavenCentral()
    maven { url 'https://repo.spring.io/milestone' }
}

ext {
    set('springCloudVersion', "2020.0.0")
}

dependencies {
    implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

 

 

 

RouteLocator Bean 생성

@Configuration
public class RouteLocatorConfig {

    @Bean
    public RouteLocator routeLocator(RouteLocatorBuilder builder) {
        return builder
                .routes()
                    .route("devsky-api-01", p -> p.path("/api01/**")
                            .uri("http://localhost:8081"))

                    .route("devsky-api-02", p -> p.path("/api02/**")
                            .uri("http://localhost:8082"))

                .build();
    }

}

 

위의 형태가 어떠한 필터나 추가 기능 없이 기본적인 라우팅만 구현한 모습이다.

이 게이트웨이의 uri가 http://localhost:8080  이라고 한다면

http://localhost:8080/api01/** 으로 접근하면 게이트웨이에서 http://localhost:8081/api01/** 로 라우팅 해 줄 것이다.

 

 

(1) path 이외 추가 조건을 걸어 라우팅

위의 예시에서는 path 조건에 따라 라우팅이 되도록 작성했다. 그러나 추가적으로 header 여부, 특정 시간 등의 조건을 추가하고 싶을 수 있다. 예를 들면 오늘부터 내일 오후 6시 사이에만 라우팅을 동작하도록 하는 등의 조건을 걸어주는 것이다. 이벤트 페이지 라우팅등에 쓰일 법한 조건이다.

@Configuration
public class RouteLocatorConfig {

    @Bean
    public RouteLocator routeLocator(RouteLocatorBuilder builder) {
        return builder
                .routes()
                    .route("devsky-api-01", p -> p
                            .path("/api01/**")
                            .and().header("custom_header")
                            .and().between(getTime("20210125"), getTime("20200126"))
                            .uri("http://localhost:8081"))

                    .route("devsky-api-02", p -> p.path("/api02/**")
                            .uri("http://localhost:8082"))

                .build();
    }
    
    private ZonedDateTime getTime(String yyyyMMdd) {
        return ZonedDateTime.parse(yyyyMMdd, DateTimeFormatter.BASIC_ISO_DATE);
    }

}

위와 같이 and() 연산자로 연결해가며 계속해서 조건을 추가할 수 있다. 이 외에도 쿠키 지정, weight라는 비율 지정 등의 조건도 추가 가능하다.

 

@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
    return builder
            .routes()
                .route("devsky-openapi-01", p -> p
                        .path("/openapi/**")
                        .and().weight("openapi", 7)
                        .uri("http://localhost:7070"))
                
                .route("devsky-openapi-02", p -> p
                        .path("/openapi/**")
                        .and().weight("openapi", 3)
                        .uri("http://localhost:3030"))

            .build();
}

위는 두 개의 path 조건이 같지만 weight 를 추가하여

localhost:7070 쪽으로 70%, localhost:3030 쪽으로 30% 비율로 배분되도록 하였다.

canary test (카나리아 테스트) 를 적용하기에 적합한 기능이 아닐까 싶다.

 

 

 

(2) filter 추가

@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder, TestGatewayFilter testGatewayFilter) {
    return builder
            .routes()
                .route("devsky-api-01", p -> p.path("/api01/**")
                        .filters(f -> f.stripPrefix(1))
                        .uri("http://localhost:8081"))

                .route("devsky-api-02", p -> p.path("/api02/**")
                        .filters(f -> f
                                .addRequestParameter("param", "addparam")
                                .filter(testGatewayFilter.testFilter())
                        .uri("http://localhost:8082"))

            .build();
}

다양한 기본 지원 필터를 따로 구현 없이 사용 가능하다.

위에서 예시로 작성한 stripPrefix는 지정한 수만큼의 prefix를 제거해준다.

 

예를들어 첫번째 gateway url이 localhost:8080일 경우, http://localhost:8080/api01/datas 를 호출하면 http://localhost:8081/api01/datas 로 라우팅된다.

이는 모든 최상위 path에 /api01을 가지고 있어야 한다는 이야기가 된다. api01 모듈의 controller mapping 주소에 매번 "/api01" 을 포함시켜줘도 되겠지만, 그렇게 하고 싶지 않아서 stripPrefix(1) 로 첫번째 prefix인 "/api01" 을 제거해 주었다.

위와 같이 적용하면 http://localhost:8080/api01/datas 호출 시 http://localhost:8081/datas 로 라우팅된다.

 

그 외에 두번째 route에서 보이듯이 파라미터 추가 필터 및 커스텀하게 구현한 필터 testGatewayFilter를 추가해주는 것도 가능하다. 전역 필터가 아닌 route마다 각각 따로 추가해줄 커스텀한 필터의 경우 GatewayFilter를 구현해주면 된다.

이 gatewayFilter의 구현은 다음에 따로 포스팅해보려고 한다.

 

기본 메서드로 제공되는 필터들이 꽤 많이 있으니 쭉 둘러보고 원하는 기능이 있으면 적용해서 사용하면 될 듯 하다.

기본으로 지원되는 필터 메서드. IDE의 자동완성은 최고입니다.

 

 

(3) metadata

@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
    return builder
            .routes()
                .route("devsky-api-01", p -> p.path("/api01/**")
                        .uri("http://localhost:8081"))

                .route("devsky-api-02", p -> p.path("/api02/**")
                        .metadata(RouteMetadataUtils.RESPONSE_TIMEOUT_ATTR, 3000)
                        .metadata(RouteMetadataUtils.CONNECT_TIMEOUT_ATTR, 1000)
                        .uri("http://localhost:8082"))
            .build();
}

메타데이터 설정으로 response timeout 과 connect timeout을 지정해줄 수 있다.

response timeout은 라우팅해준 곳에서 응답해주는데 기다려줄 최대 시간이며, connect timeout은 라우팅해줄 곳을 연결하는데 기다려줄 최대 시간이다. 둘 다 밀리초로 지정된다.

 

예를 들어서 devsky-api-02 가 어떠한 이유로 잠시 네트워크 연결이 불안정해졌다고 할 때, 1000ms(1초) 가 지나면 connect timeout으로 오류를 내려준다.

만약 devsky-api-02 연결이 정상적으로 이루어졌는데, devsky-api-02 가 시스템 자원 부족으로 매우 느려져서 3000ms(3초) 이내에 응답을 주지 못하면 response timeout 으로 오류를 내려주게 된다.

 

전역 설정의 경우는 properties(yaml)에 간단히 작성이 가능하다.

spring:
  cloud:
    gateway:
      httpclient:
        connect-timeout: 1000
        response-timeout: 5s

 

 

 

 

 

 

이런 설정들은 실제 서비스에서는 gateway 관리페이지를 따로 두고, 외부에서 값을 주입받도록 하는 것이 가장 좋은 방법일 것 같다. 그래야 이벤트페이지나 점검페이지 하나 돌릴때마다 소스를 매번 수정배포빌드재시작하는 수고로움을 줄일 수 있지 않을까...

마치 aws의 api gateway가 관리자 화면에서 버튼 클릭으로 설정을 추가했다 뺐다 하는 것처럼.

 

기회가 된다면 다음번엔 그러한 방식으로 구현을 해봐야 겠다!