2021. 1. 27. 00:37ㆍSPRING
[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의 구현은 다음에 따로 포스팅해보려고 한다.
기본 메서드로 제공되는 필터들이 꽤 많이 있으니 쭉 둘러보고 원하는 기능이 있으면 적용해서 사용하면 될 듯 하다.
(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가 관리자 화면에서 버튼 클릭으로 설정을 추가했다 뺐다 하는 것처럼.
기회가 된다면 다음번엔 그러한 방식으로 구현을 해봐야 겠다!
'SPRING' 카테고리의 다른 글
[Spring Cloud Gateway] Zuul 대신 spring cloud gateway (0) | 2021.01.26 |
---|---|
[Spring] @Transactional 이란? (1) | 2019.10.06 |
[Spring] IntelliJ + Spring boot 프로젝트 생성 (0) | 2019.09.29 |