2019년 5월 12일, 2.1.0.RELEASE 버전 기준
이 프로젝트는 Spring 5, Spring Boot 2 및 Project Reaactor를 포함하는 스프링 생태계 위에 구축된 API Gateway를 제공합니다. Spring Cloud Gateway는 API 라우팅 및 보안, 모니터링/메트릭, 탄력성과 같은 크로스커팅 관심사를 해결하는 간단하지만 효과적인 방법을 제공하는 것을 목표로 합니다.
1. Spring Cloud Gateway를 어떻게 포함할 것인가
Spring Cloud Gateway를 프로젝트에 포함하기 위해 org.springframework.cloud group과 spring-cloud-starter-gateway 로 이루어진 starter를 사용하십시오. https://projects.spring.io/spring-cloud/ 를 확인하여 현재 Spring Cloud 릴리즈 트레인에서 빌드 시스템을 구성하는 방법을 확인하십시오.
중요! - Spring Cloud Gateway 는 Spring Boot 및 Spring webflux 가 제공하는 Netty 런타임을 필요로 합니다. 고전적인 서블릿 컨테이너나 WAR로 빌드된 경우 동작하지 않습니다.
2. 용어
- Route(경로) : 게이트웨이의 기본 빌딩 블록을 라우팅합니다. 이것은 ID, 목적지 URI, 조건부(predicate)의 집합 및 필터들의 집합으로 정의됩니다. 종합(aggegate)된 조건부가 참일 경우 경로를 매치합니다.
- Predicate(조건부) : 이것은 Java 8 의 Function Predicate 입니다. 입력 유형은 Spring Framework SeverWebExchange 입니다. 개발자가 헤더나 파라미터 등과 같은 HTTP 요청으로부터 어떤 것이든 매치할 수 있도록 해 줍니다.
- Filter(필터) : 특정한 factory 로 생성된 Spring Framework GatewayFilter 인스턴스입니다. 이곳에서 내려보내기(downstream) 요청을 보내기 전후에 변경을 가할 수 있습니다.
3. 어떻게 동작하는 것인가?
클라이언트는 Spring Cloud Gateway 로 요청을 합니다. Gateway Handler Mapping 이 Route에 일치하는 요청이라고 판단하면, Gateway Web Handler 로 보냅니다. 이 핸들러는 요청과 관련된 filter chain 을 통해 요청을 보냅니다. 필터가 점선으로 구분된 이유는, 필터는 proxy 요청이 보내지기 전 / 후로 로직을 실행하기 때문입니다. 모든 "사전" 필터 로직이 실행 된 후, Proxy 요청이 만들어 집니다. 프록시 요청이 만들어지면 "사후" 필터 로직이 실행됩니다.
참고 : 포트 없는 Route 로 규정된 URI는 HTTP나 HTTPS에 따라 80 혹은 443 포트를 기본적으로 가집니다.
4. 라우트 조건부 팩토리 (Route Predicate Factories)
Spring Cloud Gateway는 Spring Webflux HandlerMapping 기반 구조의 일부로서 경로를 매치합니다. Spring Cloud Gateway 는 다양한 라우트 조건부 팩토리를 내장하고 있습니다. 모든 조건(predicates)은 HTTP 요청의 다른 속성들과 매치됩니다. 복수의 라우트 조건부 팩토리들을 결합할 수 있으며, 논리적 and 조건으로 조합됩니다.
4.1 After Route Predicate Factory
After Route Predicate Factory는 datetime 이라는 하나의 파라미터를 가집니다. 이 predicate는 현재 datetime보다 이후에 일어난 요청과 매치됩니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: after_route
uri: http://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
이 경로는 덴버 시간으로 2017년 1월 20일 17시 42분 47초 789 (GMT-07:00) 이후에 오는 요청과 매치됩니다.
4.2 Before Route Predicate Factory
Before Route Predicate Factory는 datetime 이라는 하나의 파라미터를 가집니다. 이 predicate는 현재 datetime보다 이전에 일어난 요청과 매치됩니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: before_route
uri: http://example.org
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
이 경로는 덴버 시간으로 2017년 1월 20일 17시 42분 47초 789 (GMT-07:00) 이전에 오는 요청과 매치됩니다.
4.3 Between Route Predicate Factory
Between Route Predicate Factory는 두 개의 파라미터 datetime1 과 datetime2를 가집니다. 이 prediate 는 dateime1 이후 datetime2 이전에 일어난 요청과 매치됩니다. datetime2는 반드시 datetime1 이후여야 합니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: between_route
uri: http://example.org
predicates:
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
이 경로는 덴버 시간으로 2017년 1월 20일 17시 42분 47초 789 (GMT-07:00) 부터 덴버 시간으로 2017년 1월 21일 17시 42분 47초 789 (GMT-07:00) 까지 일어난 요청과 매치됩니다. 이것은 유지보수 창을 띄우거나 할 때 유용합니다.
4.4 Cookie Route Predicate Factory
Cookie Route Predicate Factory 는 두 개의 파라미터, cookie name과 정규식을 가집니다. 이 predicate는 해당하는 이름과 정규식에 일치하는 값을 가지는 쿠키와 매치됩니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://example.org
predicates:
- Cookie=chocolate, ch.p
이 경로는 ch.p라는 정규식에 일치하는 값을 가지는 chocolate 라는 이름의 쿠키와 매치됩니다.
4.5 Header Route Predicate Factory
Header Route Predicate Factory는 두 개의 파라미터, header name과 정규식을 가집니다. 이 predicate는 해당하는 이름과 정규식에 일치하는 값을 가지는 header 와 매치 됩니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: header_route
uri: http://example.org
predicates:
- Header=X-Request-Id, \d+
이 경로는 요청이 X-Request-Id 라는 이름을 가지고 값이 (하나 이상의 숫자를 의미하는) \d+ 정규식에 일치할 경우에 매치됩니다.
4.6 Host Route Predicate Factory
Host Route Predicate Factory는 호스트명 패턴의 리스트를 하나의 파라미터로 가집니다. 패턴은 . 을 구분자로 가지는 Ant style 패턴입니다. 이 predicate는 패턴과 일치하는 Host 헤더와 매치 됩니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://example.org
predicates:
- Host=**.somehost.org,**.anotherhost.org
{sub}.myhost.org 와 같은 URI 템플릿 변수도 지원합니다.
이 경로는 요청이 www.somehost.org 혹은 beta.somehost.org 나 www.anotherhost.org 일 때 매치 됩니다.
이 predicate 는 URI template 변수(위의 예제에서 정의된 sub 와 같은 것들)를 이름과 값의 Map으로 추출하여 ServerWebExchange.getAttributes()에 ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE에 정의된 key로 넣습니다. (주 : 여기의 50, 229 라인 참조) 이 값은 GatewayFilter Factory에서 사용 가능합니다.
4.7 Method Route Predicate Factory
Method Route Predicate Factory는 하나의 파라미터, 일치하는 HTTP 메소드를 가집니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: method_route
uri: http://example.org
predicates:
- Method=GET
이 경로는 request가 GET메소드일 경우에 일치합니다.
4.8 Path Route Predicate Factory
Path Route Predicate Factory는 두 개의 파라미터, PathMatcher의 리스트와 선택적인 flag로 matchOptionalTrailingSeparator를 가집니다.
applicaton.yml
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://example.org
predicates:
- Path=/foo/{segment},/bar/{segment}
이 경로는 요청의 path가 예를 들어 /foo/1 혹은 /foo/bar 혹은 /bar/baz 일 때 매치 됩니다.
이 predicate 는 URI template 변수(위의 예제에서 정의된 segment 와 같은 것들)를 이름과 값의 Map으로 추출하여 ServerWebExchange.getAttributes()에 ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE에 정의된 key로 넣습니다. 이 값은 GatewayFilter Factory에서 사용 가능합니다.
이러한 변수들을 쉽게 접근하기 위한 유틸리티 메소드 사용이 가능합니다.
Map<String, String> uriVariables = ServerWebExchangeUtils.getPathPredicateVariables(exchange);
String segment = uriVariables.get("segment");
4.9 Query Route Predicate Factory
Query Route Predicate Factory는 두 개의 파라미터를 가집니다. param은 필수이며, regexp는 선택적입니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: query_route
uri: http://example.org
predicates:
- Query=baz
이 경로는 요청이 쿼리 파라미터 baz를 포함할 경우 매치됩니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: query_route
uri: http://example.org
predicates:
- Query=foo, ba.
이 경로는 요청이 값이 ba. 정규식에 일치하는 값을 가지는 foo 쿼리 파라미터를 가질 경우에 일치합니다. 그래서 bar나 baz가 foo의 값일 때는 매치됩니다.
4.10 RemoateAddr Route Predicate Factory
RemoteAddr Route Predicate Factory는 (최소 1개의 값을 가지는) CIDR-notation (IPv4 혹은 IPv6) 문자열 리스트를 파라미터로 가집니다. 예) 192.168.0.1/16 (192.168.0.1 은 IP 주소, 16은 서브넷 마스크)
application.yml
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: https://example.org
predicates:
- RemoteAddr=192.168.1.1/24
이 경로는 원격 주소(주 : 클라이언트 IP)가 예를 들어 192.168.1.10 일 경우 매치됩니다.
4.10.1 원격주소가 resolve 되는 방식 수정
기본적으로 RemoteAddr Route Predicate Factory는 들어오는 요청에 있는 원격 주소를 이용합니다. 이것은 스프링 클라우드가 프록시 계층 뒤에 위치할 경우 실제 클라이언트 IP와 일치하지 않습니다.
커스텀 RemoteAddressResolver를 설정하여 원격 주소가 resolve 되는 방식을 수정할 수 있습니다. Spring Cloud Gateway 는 X-Forwarded-For header 를 기반으로 하는 XForwardedRemoteAddressResolver를 기본 방식은 아니지만 원격 주소 확인용으로 제공합니다.
XForwardedRemoteAddressResolver 는 보안 측면에서 달리 접근하는 두 개의 정적 생성자 메소드를 가지고 있습니다.
XForwardedRemoteAddressResolver::trustAll 은 X-Forward-For 헤더의 첫 번째 IP를 항상 취하는 RemoteAddressResolver 를 반환합니다. 이 접근 방식은 악의적인 클라이언트가 resolver가 받아들이는 X-Forward-For의 초기값을 설정할 수 있어, 스푸핑에 취약합니다.
XForwardedRemoteAddressResolver::maxTrustedIndex 는 Spring Cloud Gateway 의 앞 단에서 구동되는 신뢰할 수 있는 인프라스트럭쳐의 숫자와 관계 있는 인덱스를 취합니다. 예를 들어서 Spring Cloud Gateway가 HAProxy 를 통해서만 접근할 수 있다면 값은 1을 사용해야 합니다. Spring Cloud Gateway에 접근하기 전에 2개 홉의 신뢰할 수 있는 인프라스트럭쳐가 필요하다면 값은 2를 사용해야 합니다.
아래와 같은 헤더가 주어졌다면
X-Forwarded-For: 0.0.0.1, 0.0.0.2, 0.0.0.3
아래의 maxTrustedIndex 값은 다음과 같은 원격 주소를 생성합니다.
maxTrustedIndex | 결과 |
[Interger.MIN_VALUE,0] | (유효하지 않음, 초기화 시 IllegalArgumentException) |
1 | 0.0.0.3 |
2 | 0.0.0.2 |
3 | 0.0..0.1 |
[4, Integer.MAX_VALUE] | 0.0.0.1 |
자바 설정 사용시
GatewayConfig.java
RemoteAddressResolver resolver = XForwardedRemoteAddressResolver
.maxTrustedIndex(1);
...
.route("direct-route",
r -> r.remoteAddr("10.1.1.1", "10.10.1.1/24")
.uri("https://downstream1")
.route("proxied-route",
r -> r.remoteAddr(resolver, "10.10.1.1", "10.10.1.1/24")
.uri("https://downstream2")
)
5. 게이트웨이 필터 팩토리(GatewayFilter Factories)
라우트 필터는 들어오는 HTTP 요청이나 나가는 HTTP 응답을 어떤 식으로는 수정할 수 있게 해 줍니다. 라우트 필터는 특정 route 범위입니다. Spring Cloud Gateway 는 다양한 GatewayFilter Factory를 내장하고 있습니다.
참고 : 아래의 필터를 어떻게 사용하는지 예제는, unit tests 를 참고하세요
5.1 AddRequestHeader GatewayFilter Factory
AddRequestHeader GatewayFilter Factory 는 이름과 값을 파라미터로 가집니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
filters:
- AddRequestHeader=X-Request-Foo, Bar
이것은 일치하는 모든 요청에 대해 X-Request-Foo: Bar 헤더를 내려보내는(downstream) 요청에 추가할 것입니다.
5.2 AddRequestParameter GatewayFilter Factory
AddRequestParameter GatewayFilter Factory 는 이름과 값을 파라미터로 가집니다
application.yml
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: https://example.org
filters:
- AddRequestParameter=foo, bar
이것은 일치하는 모든 요청에 대해 foo=bar 를 내려보내는 요청의 쿼리 스트링에 추가할 것입니다.
5.3 AddResponseHeader GatewayFilter Factory
AddResponseHeader GatewayFilter Factory는 이름과 값을 파라미터로 가집니다
application.yml
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
filters:
- AddResponseHeader=X-Response-Foo, Bar
이것은 일치하는 모든 요청에 대해 X-response-Foo: Bar 헤더를 내려보내는 응답의 헤더에 추가할 것입니다.
5.4 Hystrix GatewayFilter Factory
Hystrix 는 서킷 브레이커 패턴을 구현한 넷플릭스의 라이브러리입니다. Hystrix GatewayFilter 는 gateway 경로에 서킷프레이커를 도입할 수 있게 하여, 서비스가 실패에 종속되는 것을 방지하고 다운스트림 실패 이벤트에서 fallback 응답을 제공할 수 있도록 해 줍니다.
Hystrix GatewayFilter 를 프로젝트에서 활성화하기 위하여 spring-colud-starter-netflix-hystrix 의존성을 추가하세요
Hystrix GatewayFilter Factory는 HystrixCommand의 이름을 단일 파라미터로 필요로 합니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: hystrix_route
uri: https://example.org
filters:
- Hystrix=myCommandName
이것은 HystrixCommand에 남아 있는 필터를 myCommandName이라는 커맨드명으로 감쌉니다.
Hystrix 필터는 선택적으로 fallbackUri 파라미터를 받을 수 있습니다. 현재, forward: 스키마 URI만을 지원합니다. fallback이 호출되면, 요청은 URI에 일치하는 컨트롤러로 포워딩 됩니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: hystrix_route
uri: lb://backing-service:8088
predicates:
- Path=/consumingserviceendpoint
filters:
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forward:/incaseoffailureusethis
- RewritePath=/consumingserviceendpoint, /backingserviceendpoint
이것은 Hystrix fallback 이 호출되었을 때 /incaseoffailureusethis 로 포워딩 됩니다. 이 예제는 (선택적으로) Spring Cloud Netflix Ribbon 로드밸런싱을 목적지 URI에 붙은 lb 접두사를 통해 보여준다는 것을 참고하십시오.
fallbackUri 의 주된 사용 시나리오는 gateway 애플리케이션 내부의 컨트롤러나 핸들러에 사용하는 것입니다. 그렇지만, 외부 애플리케이션의 컨트롤러나 핸들러로 다시 라우팅하는 것도 가능합니다. 다음과 같이:
application.yml
spring:
cloud:
gateway:
routes:
- id: ingredients
uri: lb://ingredients
predicates:
- Path=//ingredients/**
filters:
- name: Hystrix
args:
name: fetchIngredients
fallbackUri: forward:/fallback
- id: ingredients-fallback
uri: http://localhost:9994
predicates:
- Path=/fallback
이 예제에서, 게이트웨이 애플리케이션에는 fallback 엔드포인트나 핸들러가 없습니다, 그렇지만, http://localhost:9994 에 등록된 다른 애플리케이션이 있습니다.
요청이 fallback으로 포워딩된 경우, Hystric Gateway filter는 이를 유발한 Throwable 도 전달합니다.
이것은 게이트웨이 애플리케이션에서 fallback을 다룰 때 사용할 수 있는 ServerWebExchangeUtils.HYSTRIX_EXECUTION_EXCEPTION_ATTR 속성으로 ServerWebExchange에 추가됩니다.
외부 컨트롤러/핸들러 시나리오에서 헤더는 자세한 예외와 추가될 수 있습니다. FallbackHeaders GatewayFilter Factory 섹션 에서 자세한 정보를 얻을 수 있습니다.
Hystrix 설정 (예를 들어 시간제한) 은 전역 기본값으로 설정하거나, 경로별 기반으로 Hystrix wiki에서 설정된 애플리케이션 속성값을 이용해 설정할 수 있습니다.
5초의 시간 제한을 위의 예제 경로에 추가하려면 아래와 같은 설정이 사용됩니다.
application.yml (주 : application.properties 의 오타 같다)
hystrix.command.fallbackcmd.execution.isolation.thread.timeoutInMilliseconds: 5000
5.5 FallbackHeaders GatewayFilter Factory
FallbackHeaders 팩토리는 아래의 시나리오와 같은 외부 애플리케이션의 fallbackUri 로 전달된 요청의 헤더에 존재하는 자세한 Hystrix 실행 예외를 추가할 수 있도록 한다.
application.yml
spring:
cloud:
gateway:
routes:
- id: ingredients
uri: lb://ingredients
predicates:
- Path=//ingredients/**
filters:
- name: Hystrix
args:
name: fetchIngredients
fallbackUri: forward:/fallback
- id: ingredients-fallback
uri: http://localhost:9994
predicates:
- Path=/fallback
filters:
- name: FallbackHeaders
args:
executionExceptionTypeHeaderName: Test-Header
이 예제에서, HystrixCommand 구동 중 발생한 실행 예외가 나타난 후, 요청은 localhost:9994에서 구동 중인 애플리케이션에 존재하는 fallback 엔드포인트나 핸들러로 전달된다. 예외 타입, 메시지, -만일 가능하다면- 최상위(root)의 유발한 예외 타입 과 메시지가 FallbackHeaders 필터에 의해 요청에 추가 된다.
헤더의 이름은 아래에 나타난 인자의 이름들을 설정하여 구성(config)에서 덮어 쓸 수 있다. 기본값도 딸려 있다.
- executionExceptionTypeHeaderName ("Execution-Exception-Type")
- executionExceptionMessageHeaderName ("Execution-Exception-Message")
- rootCauseExceptionTypeHeaderName ("Root-Cause-Exception-Type")
- rootCauseExceptionMessageHeaderName ("Root-Cause-Exception-Message")
Hystrix GatewayFilter Factory 섹션에서 Hystrix가 어떻게 동작하는지 더 많은 정보를 얻을 수 있다.
5.6 PrefixPath GatewayFilter Factory
PrefixPath GatewayFilter Factory는 하나의 prefix 파라미터를 가진다.
application.yml
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: https://example.org
filters:
- PrefixPath=/mypath
이것은 일치하는 모든 요청에 대해 /mypath 를 접두사로 붙인다. 그러므로 /hello 요청은 /mypath/hello 로 전송된다.
5.7 PreserveHostHeader GatewayFilter Factory
PreserveHostHeader GatewayFilter Factory 는 파라미터가 없다. 이 필요는 요청의 속성 HTTP 클라이언트가 결정한 호스트 헤더가 아닌, 원본 호스트 헤더를 보내야 하는지를 결정하기 위해 라우팅 필요가 요청해야 하는 요청의 속성을 설정합니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: preserve_host_route
uri: https://example.org
filters:
- PreserveHostHeader
5.8 RequestRateLimiter GatewayFilter Factory
RequestRateLimiter GatewayFilter Factory 는 현재 요청이 처리될 수 있는지 결정하기 위해 RateLimiter 구현을 사용한다. 만약 그렇지 않다면, (기본적으로) HTTP 429 - Too Many Requests 를 반환한다.
이 필터는 선택적으로 keyResolver 파라미터 및 파라미터별로 rate 제한기를 가진다 (아래 참조)
keyResolver 는 keyResolver 인터페이스를 구현한 bean이다. 설정에서, SpEL을 사용한 이름의 빈을 참조한다. #{@myKeyResolver} 는 myKeyResolver 이름을 가진 빈을 지칭하는 표현식이다.
KeyResolver.java
public interface KeyResolver {
Mono<String> resolve(ServerWebExchange exchange);
}
KeyResolver 인터페이스는 삽입 가능한 전략으로 요청을 제한 하기 위한 키를 파생시킨다. 미래의 마일스톤에서는, KeyResolver 의 몇몇 구현체가 있을 예정이다.
KeyResolver의 기본 구현체는 ServerWebExchange에서 Principal을 찾아 Principal.getName()을 호출하는 PrincipalNameKeyResolver이다.
기본적으로, KeyResolver가 key를 찾지 못하면, 요청은 거부 된다. 이 행동은 spring.cloud.gateway.filter.request-rate-limiter.deny-empty (true or false) 와 spring.cloud.gateway.filter.request-rate-limiter.empty-key-status-code 속성으로 조절할 수 있다.
참고 : RequestRateLimiter 는 "단축" 노테이션을 통해 설정할 수 있습니다. 아래의 예제는 유효하지 않습니다.
application.properties
# INVALID SHORTCUT CONFIGURATION
spring.cloud.gateway.routes[0].filters[0]=RequestRateLimiter=2, 2, #{@userkeyresolver}
5.8.1 Redis RateLimiter
Redis 구현은 Stripe 의 작업에 기반합니다. spring-boot-starter-data-redis-reactive 스프링 부트 스타터의 사용을 필요로 합니다.
Token Bucket 알고리즘을 사용합니다.
redis-rate-limiter-replenishRate는 버려지는 요청 없이 초당 얼마나 많은 요청을 사용자가 하도록 할지입니다. 이것은 token bucket이 채워지는 비율입니다.
redis-rate-limiter.burstCapacity는 1초 이내에 사용자가 요청할 수 있는 최대 숫자입니다. 이것은 token bucket이 가지고 있는 token의 숫자입니다. 이 값을 0으로 설정하면 모든 요청이 차단됩니다.
안정적인 비율은 replenishRate와 burstCapacity를 같은 값으로 설정하여 얻을 수 있습니다. burstCapacity를 replenishRate보다 높게 설정하면 임시로 버스트를 허용할 수 이습니다. 이러한 경우, rate limiter는 replenishRate에 따라 burst 사이에 허용되어야 합니다. 2개의 연속된 버스트는 요청을 버립니다. (HTTP 429 -Too Many Requests)
application.yml
spring:
cloud:
gateway:
routes:
- id: requestratelimiter_route
uri: https://example.org
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
Config.java
@Bean
KeyResolver userKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
}
이것은 사용자당 10의 요청 제한을 설정하였습니다. burst는 20이 허용됩니다. 하지만 다음 1초간 단지 10개의 요청만이 가능합니다. KeyResolver는 user 요청 파라미터를 가지는 간단한 것입니다. (주 : 프로덕션 환경에선 권장하지 않습니다)
rate limiter는 RateLimiter 빈을 구현하여 설정할 수 있습니다. 설정에서 SpEL을 사용하여 빈을 이름으로 참조할 수 있습니다. #{@myRateLimiter} 는 myRateLimiter라는 이름을 가지는 빈을 지칭하는 SpEL입니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: requestratelimiter_route
uri: https://example.org
filters:
- name: RequestRateLimiter
args:
rate-limiter: "#{@myRateLimiter}"
key-resolver: "#{@userKeyResolver}"
5.9 RedirectTo GatewayFilter Factory
RedirectTo GatewayFilter Factory는 status와 url 파라미터를 가집니다. 상태는 반드시 301과 같은 300대의 리디렉트 http code여야 합니다. url은 반드시 유효한 url이어야 합니다. 이것은 Location 헤더의 값이 됩니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: https://example.org
filters:
- RedirectTo=302, https://acme.org
이것은 Location:https://acme.org 헤더와 함께 302 상태의 요청을 리디렉트를 수행하기 위해 보냅니다.
5.10 RemoveHopByHopHeadersFilter GatewayFilter Factory
RemoveHopByHopHeadersFilter GatewayFilter Factory 는 전달된 요청에서 헤더를 제거한다. 제거되는 기본적인 헤더의 목록은 IETF 에서 얻었다.
- 기본적으로 제거되는 헤더를
- Connection
- Keep-Alive
- Proxy-Authenticate
- Proxy-Authorization
- TE
- Trailer
- Transfer-Encoding
- Upgrade
이것을 바꾸려면 spring.cloud.gateway.filter.remove-non-proxy-headers.headers 속성을 제거할 헤더의 이름의 리스트로 설정하세요
5.11 RemoveRequestHeader GatewayFilter Factory
RemoveRequestHeader GatewayFilter Factory 는 name을 파라미터로 가집니다. 이것은 지워질 헤더의 이름입니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: removerequestheader_route
uri: https://example.org
filters:
- RemoveRequestHeader=X-Request-Foo
다운스트림으로 보내지기 전 X-Request-Foo 헤더를 제거합니다.
5.12 RemoveResponseHeader GatewayFilter Factory
RemoveResponseHeader GatewayFilter는 name을 파라미터로 가집니다. 이것은 지워질 헤더의 이름입니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: removeresponseheader_route
uri: http://example.org
filters:
- RemoveResponseHeader=X-Response-Foo
이것은 gateway 클라이언트로 되돌려주기 전에 응답에서 X-Response-Foo 헤더를 제거합니다.
모든 종류의 민감한 헤더를 제거하기 위해서 이 필터를 원하는 모든 경로에 구성하여야 합니다. 또한 이 필터를 spring.cloud.gateway.default-filters를 사용해 한 번 구성하고 모든 경로에 적용할 수 있습니다.
5.13 RewritePath GatewayFilter Factory
RewritePath GatewayFilter Factory는 경로 regexp 파라미터와 replacement 파라미터를 가집니다. 요청 경로를 유연하게 재정의하기 위해 Java 정규표현식을 사용합니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: rewritepath_route
uri: https://example.org
predicates:
- Path=/foo/**
filters:
- RewritePath=/foo/(?<segment>.*), /$\{segment}
/foo/bar 라는 경로에 대해 다운스트림 요청을 만들기 전 경로는 /bar 로 설정합니다. YAML스펙에 의해 $\는 $로 치환됨을 참고하세요.
5.14 RewirteResponseHeader GatewayFilter Factory
RewriteResponseHeader Gateway Filter Factory는 name, regexp 및 replacement 파라미터를 가집니다. 응답 헤더값을 유연하게 재정의하기 위해 Java 정규표현식을 사용합니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: rewriteresponseheader_route
uri: https://example.org
filters:
- RewriteResponseHeader=X-Response-Foo, , password=[^&]+, password=***
/42?user=ford&password=omg!what&flag=true 라는 헤더 값에 대해, 다운스트림 요청을 만들기 전 /42?user=ford&password=***&flag=true 로 설정합니다. YAML스펙에 따라 $를 표현하기 위해 $\을 사용하세요.
5.15 SaveSession GatewayFilter Factory
SaveSession GatewayFilter Factory 는 호출 다운스트림을 전달하기 전 WebSession::save 동작을 강제합니다. 이는 전달되는 호출을 만들기 전 세션 상태를 저장하는 것을 보장할 필요가 있거나 지연 데이터 보관(lazy data store)을 사용하는 Spring Session과 같은 것을 이용할 때 사용하는 특별한 경우입니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: save_session
uri: https://example.org
predicates:
- Path=/foo/**
filters:
- SaveSession
Spring Security 를 Spring Session과 통합하였고, 세부적인 보안요소가 원격 프로세스로 전달되는 것을 보장하기를 원한다면, 이것은 매우 중요합니다.
5.16 SecureHeaders GatewayFilter Factory
SecureHeaders GatewayFilter Factory는 이 포스트에서 추천하는 몇 개의 헤더를 응답에 추가합니다.
아래의 헤더들을 추가합니다 (기본 값과 함께 표현되어 있습니다)
- X-Xss-Protection:1; mode=block
- Strict-Transport-Security:max-age=631138519
- X-Frame-Options:DENY
- X-Content-Type-Options:nosniff
- Referrer-Policy:no-referrer
- Content-Security-Policy:default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'
- X-Download-Options:noopen
- X-Permitted-Cross-Domain-Policies:none
기본 값들을 바꾸려면, spring.cloud.gateway.filter.secure-headers 네임스페이스에 적당한 속성을 설정하세요.
바꿀 속성:
- xss-protection-header
- strict-transport-security
- frame-options
- content-type-options
- referrer-policy
- content-security-policy
- download-options
- permitted-cross-domain-policies
5.17 SetPath GatewayFilter Factory
SetPath GatewayFilter Factory는 template 파라미터를 가집니다. path의 템플릿화된 세그먼트를 허용하여 요청 path를 조작할 수 있는 간단한 방법을 제공합니다. 이것은 스프링 프레임워크의 uri templates를 이용합니다. 여러개의 매칭 세그먼트도 허용합니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: setpath_route
uri: https://example.org
predicates:
- Path=/foo/{segment}
filters:
- SetPath=/{segment}
/foo/bar 요청 경로에 대해서 다운스트림 요청을 만들기 전 경로를 /bar 로 설정합니다.
5.18 SetResponseHeader GatewayFilter Factory
SetResponseHeader GatewayFilter Factory는 name과 value 파라미터를 가집니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: setresponseheader_route
uri: https://example.org
filters:
- SetResponseHeader=X-Response-Foo, Bar
이 필터는 모든 헤더를 추가하는 것이 아니라 주어진 이름으로 치환합니다. 그러므로 다운스트림 서버가 X-Response-Foo: 1234 로 응답한 경우, 이것은 게이트웨이 클라이언트가 수신하는 X-Response-Foo: Bar 로 바뀝니다.
5.19 SetStatus GatewayFilter Factory
SetStatus GatewayFilter Factory 는 status 를 단일 파라미터로 가집니다. 이것은 반드시 유효한 스프링의 HttpStatus 여야 합니다. 404와 같은 정수값 혹은 열거형으로 표현되는 문자 NOT_FOUND 등이 될 수 있습니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: setstatusstring_route
uri: https://example.org
filters:
- SetStatus=BAD_REQUEST
- id: setstatusint_route
uri: https://example.org
filters:
- SetStatus=401
두 경우 모두 응답의 HTTP 상태는 401이 될 것입니다.
5.20 StripPrefix GatewayFilter Factory
StripPrefix GatewayFilter 는 parts 를 파라미터로 가집니다. parts 파라미터는 다운스트림으로 보내기 전 요청에서 떼어 내는 경로의 숫자를 나타냅니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: nameRoot
uri: http://nameservice
predicates:
- Path=/name/**
filters:
- StripPrefix=2
게이트웨이를 통해 /name/bar/foo 로 요청이 만들어지면, nameservice 에 대한 요청은 http://nameservice/foo 와 같이 보일 것입니다.
5.21 Retry GatewayFilter Factory
Retry GatewayFilter Factory는 retries, statuses, methods 및 series 를 파라미터로 가집니다.
- retries : 반드시 시도하는 재시도 횟수
- statuses : 재시도해야 하는 HTTP 상태 코드, org.springframework.http.HttpStatus를 사용하여 표현한다.
- methods : 재시도해야 하는 HTTP 메소드. org.springframework.http.HttpMethod를 사용하여 표현한다.
- series : 재시도 할 일련의 상태 코드들, org.springframework.http.HttpStatus.Series를 사용하여 표현한다.
spring:
cloud:
gateway:
routes:
- id: retry_test
uri: http://localhost:8080/flakey
predicates:
- Host=*.retry.com
filters:
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY
retry filter는 현재 body와 함께 재시도하는 것을 지원하지 않습니다 (예 : body를 가지는 POST 혹은 PUT 요청)
retry filter를 forward: 접두사가 붙은 URL과 사용할 경우, 목적지 엔드포인트는 오류가 발생했을 때 클라이언트에 응답을 보내고 커밋을 하는 일을 하지 않도록 반드시 주의해서 작성해야 합니다. 예를 들어, 목적지 엔드포인트가 컨트롤러인 경우, 대상 컨트롤러 메소드는 오류 상태코드와 함께 ResponseEntity를 반환해서는 안 됩니다. 대신, Exception을 던지거나, Error 를 발신해야 합니다. (예 : retry filter가 재시도에 의해 처리할 수 있도록 구성된 Mono.error(ex) 를 반환값으로)
5.22 RequestSize GatewayFilter Factory
RequestSize GatewayFilter Factory는 요청 크기가 허용된 제한보다 클 경우 다운스트림 서비스에 도달하는 것을 제한할 수 있습니다. 이 필터는 bytes로 정의된 요청의 허용 가능한 크기 제한을 RequestSize 파라미터로 가집니다.
spring:
cloud:
gateway:
routes:
- id: request_size_route
uri: http://localhost:8080/upload
predicates:
- Path=/upload
filters:
- name: RequestSize
args:
maxSize: 5000000
RequestSize GatewayFilter Factory는 크기 때문에 요청이 거부되었을 경우에, 추가적인 헤더와 함께 응답 상태를 413 Payload Too Large 로 설정 합니다. 아래는 errorMessage 의 예시입니다.
errorMessage : Request size is larger than permissible limit. Request size is 6.0MB where permissible limit is 5.0MB
참고 : 라우트 정의에 필터 인자로 제공되는 것이 없으면 기본적인 요청의 크기는 5MB 입니다.
5.23 Modify Request Body GatewayFilter Factory
이 필터는 베타입니다. API는 미래에는 바뀔 수도 있습니다.
이 필터는 request body를 게이트웨이에서 다운스트림으로 보내기 전 수정하는데 쓸 수 있습니다.
이 필터는 Java DSL 로만 구성 가능합니다.
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("rewrite_request_obj", r -> r.host("*.rewriterequestobj.org")
.filters(f -> f.prefixPath("/httpbin")
.modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE,
(exchange, s) -> return Mono.just(new Hello(s.toUpperCase())))).uri(uri))
.build();
}
static class Hello {
String message;
public Hello() { }
public Hello(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
5.24 Modify Response Body GatewayFilter Factory
이 필터는 베타입니다. API는 미래에는 바뀔 수도 있습니다.
이 필터는 response body를 클라이언트로 돌려 보내기 전 수정하는데 쓸 수 있습니다.
이 필터는 Java DSL 로만 구성 가능합니다.
@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
return builder.routes()
.route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org")
.filters(f -> f.prefixPath("/httpbin")
.modifyResponseBody(String.class, String.class,
(exchange, s) -> Mono.just(s.toUpperCase()))).uri(uri)
.build();
}
6. 전역 필터(Global Filters)
GlobalFilter는 GatewayFilter와 동일한 시그니처를 가집니다. 조건에 따라 모든 라우트에 적용되는 특별한 필터들이 있습니다. (이 인터페이스와 사용 예는 향후 마일스톤에서 변경될 수 있습니다.)
6.1 조합된 Global Filter와 GatewayFilter 순서
요청이 오면 (라우트와 매치 되고) Filtering Web Handler는 GlobalFilter의 모든 인스턴스와 경로별 GatewayFilter의 인스턴스를 필터 체인에 추가합니다. 이 조합된 필터 체인은 getOrder() 를 구현하거나 @Order 어노테이션을 이용해서 설정 할 수 있는 org.springframework.core.Ordered 인터페이스에 의해 정렬 됩니다.
Spring Cloud Gateway는 필터 로직 실행(3절. 어떻게 동작하는 것인가?를 보세요)을 위해 "pre"와 "post" 단계를 구분하기 때문에, 가장 높은 순위의 필터가 "pre" 단계의 첫번째이고 "post" 단계의 마지막 필터가 됩니다.
ExampleConfiguration.java
@Bean
@Order(-1)
public GlobalFilter a() {
return (exchange, chain) -> {
log.info("first pre filter");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("third post filter");
}));
};
}
@Bean
@Order(0)
public GlobalFilter b() {
return (exchange, chain) -> {
log.info("second pre filter");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("second post filter");
}));
};
}
@Bean
@Order(1)
public GlobalFilter c() {
return (exchange, chain) -> {
log.info("third pre filter");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("first post filter");
}));
};
}
6.2 Forward Routing Filter
ForwardRoutingFilter는 교환 속성 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 에서 URI를 찾습니다. URL이 forward 스킴을 가지고 있으면 (예 : forward:///localendpoint) 요청을 다루기 위해 Spring의 DispatcherHandler를 사용할 것입니다. 요청 URL의 path 부분은 forward URL의 경로로 대체될 것입니다. 수정되지 않은 원본 URL은 ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR 속성에 있는 리스트에 추가됩니다.
6.3 LoadBanlancerClient Filter
LoadBalancerClientFilter 는 교환 속성 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 에서 URI를 찾습니다. URL이 lb 스킴을 가지고 있으면 (예 : lb://myservice) 이름(앞의 예시에서 myservice)을 실제 호스트와 포트로 해석하고 동일한 속성의 URI로 치환하기 위해 Spring Cloud LoadBalancerClient를 사용합니다. 수정되지 않은 원본 url은 ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR 속성에 있는 리스트에 추가됩니다. 이 필터는 lb와 동일한 규칙이 적용되었는지 보기 위해 ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR 속성을 확인합니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: myRoute
uri: lb://service
predicates:
- Path=/service/**
기본적으로 서비스를 LoadBalancer에서 발견하지 못할 때에는 503을 반환합니다.
spring.cloud.gateway.loadbalancer.use404=true를 설정하여 Gateway가 404를 반환하도록 구성할 수 있습니다.
LoadBlancer 에서 반환되는 ServiceInstance의 isSecure 값은 Gateway로 보내는 요청에서 식별된 scheme을 재정의합니다. 예를 들어, 요청이 HTTPS를 통해서 Gateway로 왔지만 ServiceInstance 가 안전하지 않다면 다운스트림 요청은 HTTP를 통해서 생성됩니다. 반대의 경우도 가능합니다. 그러나 Gateway 구성 route에 GATEWAY_SCHEME_PREFIX_ATTR이 식별된다면, 접두사는 벗겨지고 route URL의 결과 scheme은 ServiceInstance 구성을 재정의합니다.
6.4 Netty Routing Filter
Nettty Routing Filter 는 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 교환 속성에 존재하는 URL이 http 혹은 https scheme을 가질 경우에 구동 됩니다. 다운스트림 프록시 요청을 만들기 위해 Netty HttpClient를 이용합니다. 응답은 다음 필터에서 사용할 ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR 에 넣습니다. (동일한 기능을 수행하지만 Netty를 필요로 하지 않는 실험적인 WebClientHttpRoutingFilter가 있습니다)
6.5 Netty Write Response Filter
NettyWriteResponseFilter 는 ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR 교환 속성에 HttpClientResponse가 있을 경우 구동됩니다. 다른 모든 필터들이 완료되면 실행되며 Gateway 클라이언트 응답에 프록시 응답을 기록합니다. (동일한 기능을 수행하지만 Netty를 필요로 하지 않는 실험적인 WebClientWriteResponseFilter가 있습니다)
6.6 RouteToRequestUrl Filter
RouteToRequestUrlFilter는 ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR 교환 속성에 Route 객체가 존재할 경우 구동됩니다. 요청 URI에 기반한 새로운 URI를 만들지만 Route 객체의 URI 속성을 가지고 업데이트 합니다. 새로운 URI는 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 교환 속성에 위치합니다.
URI가 lb:ws://serviceis 와 같은 scheme 접두사를 가지고 있다면, lb scheme는 URI에서 벗겨지고 필터 체인에서 나중에 사용하기 위해 ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR에 위치합니다.
6.7 Websocket Routing Filter
Websocket Routing Filter 는 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 교환 속성에 ws 혹은 wss scheme가 존재할 때 구동 됩니다. Spring Web Socket 인프라 스트럭쳐를 이용하여 웹소켓 요청 다운스트림을 재전송합니다.
웹소켓은 lb:ws://serviceid와 같이 lb 접두사가 붙은 URI를 통해 로드밸런싱 될 수 있습니다.
만약 SockJS를 일반 http를 통해 fallback으로 사용하고 있다면, 웹소켓 라우트 뿐 아니라 일반 HTTP 라우트도 설정해 주어야 합니다.
application.yml
spring:
cloud:
gateway:
routes:
# SockJS route
- id: websocket_sockjs_route
uri: http://localhost:3001
predicates:
- Path=/websocket/info/**
# Normwal Websocket route
- id: websocket_route
uri: ws://localhost:3001
predicates:
- Path=/websocket/**
6.8 Gateway Metrics Filter
Gateway Metrics 를 활성화하기 위해서는 spring-boot-starter-actuator 를 프로젝트 의존성에 추가해야 합니다. 그러면 spring.cloud.gateway.metrics.enabled 를 false로 설정하지 않는다면 기본적으로 Gateway Metrics Filter가 구동됩니다. 이 필터는 "gateway.requests" 라는 이름의 타이머 메트릭을 아래의 태그와 함께 추가합니다.
- routeId : 라우트 id
- routeUri : API가 라우트 될 URI
- outcome : HttpStatus.Series 에 의해 분류되는 결과
- status : 클라이언트로 반환되는 요청의 Http Status
이 메트릭들은 /actuator/metrics/gateway.requests 에서 얻을 수 있으며 Grafana 대시보드를 생성하기 위해 프로메테우스와 간편하게 통합할 수 있습니다.
프로메테우스 엔드포인트를 활성화하기 위해서는 micrometer-registry-prometheus 를 프로젝트 의존성에 추가하세요.
6.9 Making An Exchange As Routed
Gateway 가 ServerWebExchange를 라우트하고 나면 교환 속성에 gatewayAlreadyRouted를 추가하여 교환이 "라우트 됨"으로 표시할 것입니다. 요청이 라우트 된 것으로 표시되고 나면, 다른 라우팅 필터들은 기본적으로는 필터를 건너 뛰고, 해당 요청을 다시 라우트하지 않습니다, 교환이 이미 라우트 되었는지 확인 하거나, 교환을 라우트 된 것으로 표시하는데 간편하게 사용하는 방법이 있습니다.
- ServerWebExchangeUtils.isAlreadyRouted 는 ServerWebExchange 객체가 이미 라우트되었는지 확인합니다.
- ServerWebExchangeUtils.setAlreadyRouted 는 ServerWebExchange 객체를 이미 라우트되었다고 표시합니다.
7. 전송계층보안(TLS / SSL)
Gateway 는 아래의 일반적인 스프링 서버 구성을 통해서 https 요청을 수신할 수 있습니다. 예를 들어:
application.yml
server:
ssl:
enabled: true
key-alias: scg
key-store-password: scg1234
key-store: classpath:scg-keystore.p12
key-store-type: PKCS12
Gateway 라우트는 http 및 https 백엔드로 라우트 될 수 있습니다. https 백엔드로의 라우팅을 하면 Gateway는 아래의 구성으로 모든 다운스트림 인증서를 신뢰하도록 설정할 수 있습니다.
application.yml
spring:
cloud:
gateway:
httpclient:
ssl:
trustedX509Certificates:
- cert1.pem
- cert2.pem
Spring Cloud Gateway 가 신뢰할 수 있는 인증서와 함께 프로비저닝 되지 않았다면 기본 트러스트 저장소를 사용 합니다 (java.net.ssl.trustStore 로 재정의 될 수 있습니다)
7.1 TLS Handshake
Gateway 는 백엔드로 라우트하기 위한 클라이언트 풀을 유지합니다. https를 통해서 통신할 때 클라이언트는 TLS 핸드쉐이크를 초기화합니다. 이 핸드쉐이크에는 몇 가지 타임아웃이 관련되어 있습니다. 이 타임아웃들은 설정이 가능합니다(기본값은 다음과 같습니다)
application.yml
spring:
cloud:
gateway:
httpclient:
ssl:
handshake-timeout-millis: 10000
close-notify-flush-timeout-millis: 3000
close-notify-read-timeout-millis: 0
8. Configuration
Spring Cloud Gateway 설정은 RouteDefinitionLocator 의 모음에 의합니다.
RouteDefinitionLocator.java
public interface RouteDefinitionLocator {
Flux<RouteDefinition> getRouteDefinitions();
}
기본적으로, PropertiesRouteDefinitionLocator 가 스프링 부트의 @ConfigurationProperties 메커니즘을 활용해 속성 성을 불러옵니다.
위의 설정 예제들은 모두 명명된 것 대신 위치 기반 인자를 사용하는 단축 표기법을 사용합니다. 아래의 두 예제는 동일합니다.
application.yml
spring:
cloud:
gateway:
routes:
- id: setstatus_route
uri: http://example.org
filters:
- name: SetStatus
args:
status: 401
- id: setstatusshortcut_route
uri: http://example.org
filters:
- SetStatus=401
Gateway 의 활용면에서 속성값은 적절한 방법입니다, 그렇지만 일부 프로덕션 사용 케이스에서는 데이터베이스와 같은 외부소스로부터 구성을 불러오는 것이 이득일 수도 있습니다. 장래의 마일스톤버전은 Redis, MongoDB, Cassandra와 같은 Spring Data Repositories 기반의 RouteDefinitionLocator 구현을 할 예정입니다.
8.1 Fluent Java Routes API
Java에서 간결한 구성을 하기 위해, RouteLocatorBuilder bean에는 fluent API가 정의되어 있습니다.
GatewaySampleApplication.java
// static imports from GatewayFilters and RoutePredicates
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder, ThrottleGatewayFilterFactory throttle) {
return builder.routes()
.route(r -> r.host("**.abc.org").and().path("/image/png")
.filters(f ->
f.addResponseHeader("X-TestHeader", "foobar"))
.uri("http://httpbin.org:80")
)
.route(r -> r.path("/image/webp")
.filters(f ->
f.addResponseHeader("X-AnotherHeader", "baz"))
.uri("http://httpbin.org:80")
)
.route(r -> r.order(-1)
.host("**.throttle.org").and().path("/get")
.filters(f -> f.filter(throttle.apply(1,
1,
10,
TimeUnit.SECONDS)))
.uri("http://httpbin.org:80")
)
.build();
}
이 스타일은 좀 더 많은 커스텀 조건부 assertion 을 허용합니다. RouteDefinitionLocator bean에 정의된 조건자는 논리적 AND를 사용하여 조합됩니다. flutent API를 사용하면 Predicate 클래스에서 and(), or() 및 negate() 연산자를 사용할 수 있습니다.
8.2 DiscoveryClient Route Definition Locator
Gateway 는 DiscoveryClient 호환 서비스 레지스트리에 등록된 서비스를 기반으로 라우트를 생성하기 위해 구성될 수 있습니다.
기본적인 조건자는 /serviceId/** 패턴에 정의된 path 조건자입니다. serviceId는 DiscoveryClient에 등록된 서비스의 id입니다.
기본적인 필터는 정규 표현식 /serviceId/(?<remaining>.*) 에 대한 재정의 path filter 이며 치환되는 값는 /${remainning} 입니다. 요청이 다운스트림으로 보내지기 전에 서비스 id를 벗겨내는 것 뿐입니다.
DiscoveryClient 라우트에서 사용되는 조건부 and/or 필터들을 수정하고 싶다면 spring.cloud.gateway.discovery.locator.prdicates[x] 및 spring.cloud.gateway.discovery.locator.filters[y]를 설정하여 할 수 있습니다. 이렇게 할 때 위에서 서술된 기본적인 조건자 및 필터를 포함하여야 합니다, 이러한 기능을 유지하려 할 때 아래가 그러한 예시입니다.
application.properties
spring.cloud.gateway.discovery.locator.predicates[0].name: Path
spring.cloud.gateway.discovery.locator.predicates[0].args[pattern]: "'/'+serviceId+'/**'"
spring.cloud.gateway.discovery.locator.predicates[1].name: Host
spring.cloud.gateway.discovery.locator.predicates[1].args[pattern]: "'**.foo.com'"
spring.cloud.gateway.discovery.locator.filters[0].name: Hystrix
spring.cloud.gateway.discovery.locator.filters[0].args[name]: serviceId
spring.cloud.gateway.discovery.locator.filters[1].name: RewritePath
spring.cloud.gateway.discovery.locator.filters[1].args[regexp]: "'/' + serviceId + '/(?<remaining>.*)'"
spring.cloud.gateway.discovery.locator.filters[1].args[replacement]: "'/${remaining}'"
9. Reactor Netty Access Logs
Reactor Netty Access Log 를 활성화하기 위해서는 -Dreactor-netty.http.server.accessLogEnabled=true 를 설정하세요 (반드시 스프링 부트 프로퍼티가 아닌 자바 시스템 프로퍼티로 설정하여야 합니다.)
로깅 시스템은 분리된 엑세스 로그 파일을 생성하도록 구성할 수 있습니다. 아래는 로그백 설정입니다.
logback.xml
<appender name="accessLog" class="ch.qos.logback.core.FileAppender">
<file>access_log.log</file>
<encoder>
<pattern>%msg%n</pattern>
</encoder>
</appender>
<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
<appender-ref ref="accessLog" />
</appender>
<logger name="reactor.netty.http.server.AccessLog" level="INFO" additivity="false">
<appender-ref ref="async"/>
</logger>
10. 교차 출처 리소스 공유 구성(CORS Configuration)
게이트웨이는 CORS(Cross-Origin Resource Sharing) 행위를 제어할 수 있도록 구성할 수 있습니다. "전역" CORS 설정은 Spring Framework CorsConfiguration 에 대한 URL 패턴의 Map입니다.
application.yml
spring:
cloud:
gateway:
globalcors:
corsConfigurations:
'[/**]':
allowedOrigins: "http://docs.spring.io"
allowedMethods:
- GET
위의 예제에서, CORS 요청은 docs.srping.io 으로부터 온 모든 GET 방식 경로에 대해서 요청을 허용합니다.
11. Actuator API
/gateway actuator 엔드포인트는 모니터 및 Spring Cloud Gateway 애플리케이션의 상호 작용을 가능하게 합니다. 원격에서 접근 가능하기 위해서 엔드포인트를 활성화해야하며 application properties에서 HTTP 혹은 JMX를 통해 노출하여야 합니다.
application.properties
management.endpoint.gateway.enabled=true # default value
management.endpoints.web.exposure.include=gateway
11. 1 라우트 필터 검색 (Retrieving route filters)
11. 1. 1 Global Filters
모든 경로에 적용된 global filter를 찾기 위해서는 /actuator/gateway/globalfilters 로 GET 요청을 보내야 합니다. 결과 응답은 아래와 유사한 모습일 것입니다.
{
"org.springframework.cloud.gateway.filter.LoadBalancerClientFilter@77856cc5": 10100,
"org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@4f6fd101": 10000,
"org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@32d22650": -1,
"org.springframework.cloud.gateway.filter.ForwardRoutingFilter@106459d9": 2147483647,
"org.springframework.cloud.gateway.filter.NettyRoutingFilter@1fbd5e0": 2147483647,
"org.springframework.cloud.gateway.filter.ForwardPathFilter@33a71d23": 0,
"org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@135064ea": 2147483637,
"org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@23c05889": 2147483646
}
응답은 존재하는 글로벌 필터들의 세부 정보를 포함하고 있습니다. 각각의 글로벌 필터들은 문자열로 표현된 필터 객체로 나타내어 집니다 (예 : org.springframework.cloud.gateway.filter.LoadBalancerClientFilter@77856cc5) 또한 필터체인상에서 대응하는 순서를 보여 줍니다.
11. 1. 2 Route Filters
경로에 적용된 GatewayFilter factory들을 검색하기 위해서는 /actucator/gateway/routefilters 로 GET 요청을 보내야 합니다. 결과 응답은 아래와 유사한 모습일 것입니다.
{
"[AddRequestHeaderGatewayFilterFactory@570ed9c configClass = AbstractNameValueGatewayFilterFactory.NameValueConfig]": null,
"[SecureHeadersGatewayFilterFactory@fceab5d configClass = Object]": null,
"[SaveSessionGatewayFilterFactory@4449b273 configClass = Object]": null
}
응답은 특정 경로에 적용된 GatewayFilter factory의 세부 정보를 포함하고 있습니다. 각 factory는 문자열로 표현되는 연관된 객체로 나타내어 집니다. (예 : [SecureHeadersGatewayFilterFactory@fceab5d configClass = Object]) null 값은 GatewayFilter factory 객체에 적용되지 않는 필터 체인에서 객체의 순서를 정하려고 하기 때문에 발생하는 엔드포인트 컨트롤러의 불완전한 구현으로 인한 것입니다.
11.2 Refreshing the route cache
라우트 캐시를 비우기 위해서는 /actuator/gateway/refresh 로 POST 요청을 보내야합니다. 이 요청은 BODY가 없는 200 응답을 반환할 것입니다.
11.3 게이트웨이에 정의된 라우트 검색하기(Retrieving the routes defined in the gateway)
게이트웨이에 정의된 라우트를 검색하기 위해서는 /actuator/gateway/routes 에 GET 요청을 날리십시오. 응답 결과는 아래와 유사할 것입니다.
[{
"route_id": "first_route",
"route_object": {
"predicate": "org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory$$Lambda$432/1736826640@1e9d7e7d",
"filters": [
"OrderedGatewayFilter{delegate=org.springframework.cloud.gateway.filter.factory.PreserveHostHeaderGatewayFilterFactory$$Lambda$436/674480275@6631ef72, order=0}"
]
},
"order": 0
},
{
"route_id": "second_route",
"route_object": {
"predicate": "org.springframework.cloud.gateway.handler.predicate.PathRoutePredicateFactory$$Lambda$432/1736826640@cd8d298",
"filters": []
},
"order": 0
}]
응답은 게이트웨이에 정의된 모든 라우트의 세부 정보를 포함할 것입니다. 아래의 테이블은 응답의 각 요소 (예 : 라우트) 의 구조를 보여줍니다.
Path | Type | Description |
route_id | String | 라우트 id |
route_object.predicate | Object | 라우트 조건 |
route_object.filters | Array | 라우트에 적용된 GatewayFilter Factory |
order | Number | 라우트 순서 |
11.4 특정 라우트에 대한 정보 탐색(Retrieving information about a particular route)
단일 라우트에 대해 정보를 얻기 위해서는 /actuator/gateway/routes/{id} 로 GET 요청을 보내야합니다 (예 : /actuator/gateway/routes/first_route) 응답 결과는 아래와 유사할 것입니다.
{
"id": "first_route",
"predicates": [{
"name": "Path",
"args": {"_genkey_0":"/first"}
}],
"filters": [],
"uri": "http://www.uri-destination.org",
"order": 0
}]
아래의 테이블은 응답의 구조를 보여줍니다.
Path | Type | Description |
id | String | 라우트 id |
predicates | Array | 라우트 조건의 콜렉션, 각 아이템은 이름 및 주어진 조건의 인자를 정의한다. |
filters | Array | 라우트에 적용된 필터의 콜렉션 |
uri | String | 라우트의 목적지 URI |
order | Number | 라우트 순서 |
11.5 특정 라우트를 생성하거나 삭제 (Creating and deleting a particular route)
라우트를 생성하기 위해서는 /gateway/routes/{id_route_to_create} 로 POST 요청을 route의 필드를 명시한 JSON Body와 함께 보내야 합니다 (이전 섹션 참조)
라우트를 삭제하기 위해서는 /gateway/routes/{id_route_to_delete} 로 DELETE 요청을 보내야 합니다.
(주 : 레퍼런스 원문에 /actuator 가 앞에 없는데 오타로 추정됩니다)
11.6 요약 : 모든 엔드포인트 (Recap : list of all endpoints)
아래의 테이블은 Spring Cloud Gateway actuator 엔드포인트의 요약입니다. 각 엔드포인트는 /actuator/gateway 를 기본 경로로 포함하고 있다는 것을 참고 하세요.
ID | HTTP Method | Description |
globalfilters | GET | 라우트에 등록된 전역 필터 리스트 |
routefilters | GET | 특정 라우트에 등록된 GatewayFilter factory의 리스트 |
refresh | POST | 라우트 캐시 비우기 |
routes | GET | 게이트웨이에 정의된 라우트의 리스트 |
routes/{id} | GET | 특정 라우트에 대한 정보 |
routes/{id} | POST | 새로운 라우트 추가 |
routes/{id} | DELETE | 기존 라우트를 게이트웨이에서 삭제 |
12. 개발자 가이드 (Developer Guide)
TODO : 사용자 정의 통합에 대해서 살펴본다
(주 : 레퍼런스 원문에도 아직 내용이 없습니다)
12.1 Writing Custom Route Predicate Factories
TODO : 사용자 정의 라우트 조건부 팩토리에 대한 문서
12.2 Writing Custom GatewayFilter Factories
GatewayFilter 를 작성하기 위해서 GatewayFilterFactory를 구현하여야 합니다. 상속할 수 있는 추상 클래스로 AbstractGatewayFilterFactory가 있습니다.
PreGatewayFilterFactory.java
public class PreGatewayFilterFactory extends AbstractGatewayFilterFactory<PreGatewayFilterFactory.Config> {
public PreGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
// grab configuration from Config object
return (exchange, chain) -> {
//If you want to build a "pre" filter you need to manipulate the
//request before calling chain.filter
ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
//use builder to manipulate the request
return chain.filter(exchange.mutate().request(request).build());
};
}
public static class Config {
//Put the configuration properties for your filter here
}
}
PostGatewayFilterFactory.java
public class PostGatewayFilterFactory extends AbstractGatewayFilterFactory<PostGatewayFilterFactory.Config> {
public PostGatewayFilterFactory() {
super(Config.class);
}
@Override
public GatewayFilter apply(Config config) {
// grab configuration from Config object
return (exchange, chain) -> {
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
ServerHttpResponse response = exchange.getResponse();
//Manipulate the response in some way
}));
};
}
public static class Config {
//Put the configuration properties for your filter here
}
}
12.3 Writing Custom Global Filters
TODO : 사용자 정의 글로벌 필터에 대한 문서
12.4 Writing Custom Route Locators and Writes
TODO : 사용자 정의 Locator 와 Writer 에 대한 문서
13. Spring MVC 혹은 Webflux 를 이용한 간단한 Gateway 구성 (Building a Simple Gateway Using Spring MVC or Webflux)
Spring Cloud Gateway 는 ProxyExchange라고 하는 유틸리티를 제공하여 통상적인 스프링 웹 핸들러에 메소드 파라미터로 쓸 수 있도록 해 줍니다. HTTP verb (주 : HTTP verb는 request method 와 같은 의미라고 합니다. 링크)를 반영하는 메소드를 통해 기본적인 다운스트림 HTTP 교환을 지원합니다. MVC 또한 forward() 메소드를 통해 로컬 핸들러로 전달을 지원합니다. ProxyExchange를 사용하기 위해서는 클래스패스에 정확한 모듈(spring-cloud-gateway-mvc 혹은 spring-cloud-gateway-webflux)을 포함하기만 하면 됩니다.
MVC 예제 ("/text" 로의 요청을 프록싱하여 원격 서버로 다운스트림)
@RestController
@SpringBootApplication
public class GatewaySampleApplication {
@Value("${remote.home}")
private URI home;
@GetMapping("/test")
public ResponseEntity<?> proxy(ProxyExchange<byte[]> proxy) throws Exception {
return proxy.uri(home.toString() + "/image/png").get();
}
}
Webflux 로 동일하게
@RestController
@SpringBootApplication
public class GatewaySampleApplication {
@Value("${remote.home}")
private URI home;
@GetMapping("/test")
public Mono<ResponseEntity<?>> proxy(ProxyExchange<byte[]> proxy) throws Exception {
return proxy.uri(home.toString() + "/image/png").get();
}
}
ProxyExchange에는 핸들러 메소드가 들어온 요청의 URI path를 찾거나 향상시킬 수 있도록 해 주는 간편한 메소드들이 있다. 예를 들어 경로의 뒤에 오는 요소들을 다운스트림으로 보내기 위해 추출하고 싶다면
@GetMapping("/proxy/path/**")
public ResponseEntity<?> proxyPath(ProxyExchange<byte[]> proxy) throws Exception {
String path = proxy.path("/proxy/path/");
return proxy.uri(home.toString() + "/foos/" + path).get();
}
Spring MVC 나 Webflux 의 모든 기능들이 Gateway handler methods 에서 사용 가능합니다. 그러므로 예를 들자면 요청 헤더나 쿼리 파라미터를 주입할 수도 있고 mapping 어노테이션의 선언을 통해서 들어오는 요청을 제한할 수도 있습니다. 이러한 기능에 대해서 좀 더 자세한 정보는 Spring MVC의 @RequestMapping에 대한 문서를 참조하세요.
헤더는 다운스트림 응답에 ProxyExchange에 있는 header() 메소드를 이용하여 추가할 수 있습니다.
get() 이나 다른 메소드를 이용해서 매퍼를 추가하여 응답 헤더(및 응답에서 원하는 무엇이든)를 조작할 수도 있습니다. 매퍼는 들어오는 ResponseEntity 를 취하는 Function 이며 외부로 나가는 것을 변환됩니다.
First class 지원은 다운스트림과 "proxy" 헤더(x-forwarded-*)에 전달되지 않는 "민감한" 헤더에 대해서 지원 됩니다. (기본적으로 "cookie" 와 "authorization")
'dev > Java&Spring' 카테고리의 다른 글
jib로 springboot 애플리케이션 컨테이너화 + registry 등록 (1) | 2019.09.30 |
---|---|
Java Authentication and Authorization Service (JAAS) - 요약 (0) | 2019.08.18 |
netflix hystrix-dashboard 가 뜨지 않을 때 (0) | 2019.05.08 |
Spring에서 Client Authentication (two-way TLS/SSL) 구현하기 (1) | 2019.04.07 |
Spring에서 insecure SSL 요청(RestTemplate, WebClient) (1) | 2019.04.06 |