728x90
반응형
Gateway를 만들면서 사용할 수 있는 룰들을 셋팅하는데 있어서 구글링 및 공식 Document를 보는 것에 시간이 걸려 하나씩 정리해 둔다.
참고로 사용하게 되는 Predicate Factory는 org.springframework.cloud.gateway.handler.predicate 아래에 있다. ( github.com/spring-cloud/spring-cloud-gateway/tree/2.2.x/spring-cloud-gateway-server/src/main/java/org/springframework/cloud/gateway/handler/predicate )
@Autowired
private SecureHeadersProperties secureHeadersProperties;
/**
* ApiGateway Route Test Bean
* @param builder
* @param uriConfiguration
* @return
*/
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder, UriConfiguration uriConfiguration) {
String testUrl = uriConfiguration.getTest();
String chauffeurUrl = uriConfiguration.getChauffeur();
ZonedDateTime zonedDateTime = ZonedDateTime.parse("2020-12-28T10:42:00+09:00[Asia/Seoul]", DateTimeFormatter.ISO_DATE_TIME);
// 특정 path로 들어올 경우 특정 header 값 추가
RouteLocator headerAddRouteLocator = builder.routes().route(p -> p
.path("/get", "/post")
.and().method(HttpMethod.GET, HttpMethod.POST) // 아래 methodRouteLocator 내용을 추가 적용...
.filters(f -> f.addRequestHeader("Hello", "World"))
.uri(testUrl)).build();
// 특정 host header 값을 가지고 있는 경우 fallback uri 정의
RouteLocator hostRouteLocator = builder.routes().route(p -> p
.host("*.hystrix.com")
.filters(f -> f.hystrix(config -> config
.setName("mycmd")
.setFallbackUri("forward:/fallback")
))
.uri(testUrl)).build();
// 특정 시간 이후에만 접속 가능하도록. 그 시간 이전에는 404 not found가 나옴
RouteLocator afterRouteLocator = builder.routes().route(p -> p
.after(zonedDateTime)
.uri(testUrl)).build();
// 특정 시간 이전에만 접속 가능하도록. 그 시간 이후에는 404 not found가 나옴
RouteLocator beforeRouteLocator = builder.routes().route(p -> p
.before(zonedDateTime).uri(testUrl))
.build();
// 특정 path와 특정 시간을 같이 쓰고 싶은 경우 and를 사용. 사용 방법 몰라 많이 헤맸음
RouteLocator afterAndHeaderRouteLocator = builder.routes()
.route(
p -> p
.path("/get").and().after(zonedDateTime)
.filters(f -> f.addRequestHeader("Hello", "World"))
.uri(testUrl)
)
.build();
// mypage 면서 jwt 토큰이 없는 경우에는 무조건 팅겨 내게 만들 수 있다.
RouteLocator cookieRouteLocator = builder.routes()
.route(p -> p
.path("/mypage").and().cookie("jwt", "123")
.uri(testUrl)
)
.build();
// 헤더 정보 체크. 없을 경우 404 not found
RouteLocator headerCheckRouteLocator = builder.routes()
.route(p -> p
.header("X-API-VERSION", "1.0.0")
.uri(chauffeurUrl)
)
.build();
// 특정 메소드로만 호출하도록... 허용되지 않은 메소드로 호출 시 404 not found
RouteLocator methodRouteLocator = builder.routes()
.route(p -> p
.method(HttpMethod.GET, HttpMethod.POST)
.uri(testUrl)
)
.build();
// 특정 쿼리스트링 파라미터의 값을 판별. 해당 이름의 해당 값이 없으면 404 not found
RouteLocator queryRouteLocator = builder.routes()
.route(p -> p
.query("red", "gree.") // 두 번째 파라미터 regex가 없을 경우 해당 파라미터 키만 있어도 됨.
.uri(testUrl)
)
.build();
// ip 목록에 있는 것만 허용인데 잘 안됨.
RouteLocator RemoteAddrRouteLocator = builder.routes()
.route(p -> p
.remoteAddr("192.168.1.1/16", "실제공인아이피주소", "localhost", "127.0.0.1")
.uri(testUrl)
)
.build();
// Weight 값에 따라 % 비율로 할당. 간단한 로드밸러서 역할 가능할 듯... group 이름이 같아야 함.
RouteLocator weightRouteLocator = builder.routes()
.route(p -> p
.weight("group1", 8)
.uri("https://www.naver.com")
)
.route(p -> p
.weight("group1", 2)
.uri("https://www.daum.net")
)
.build();
// 특정 파라미터를 추가하여 route, "/get" 으로 호출하면 red=get 으로 파라미터 전달.
RouteLocator addRequestParamRouteLocator = builder.routes()
.route(p -> p
.path("/{segment}")
.filters(f -> f.addRequestParameter("red", "{segment}"))
.uri(testUrl)
)
.build();
// 특정 Response Header 값을 추가하여 리턴
RouteLocator addResponseHeaderRouteLocator = builder.routes()
.route(p -> p
.path("/**")
.filters(f -> f.addResponseHeader("X-Response-Red", "Blue"))
.uri(testUrl)
)
.build();
// dedupeResponseHeader method의 javadoc에는 다음처럼 이야기 함 "A filter that removes duplication on a response header before it is returned to the client by the Gateway"
// 중복되는 헤더를 제거한다고 하는 듯. 첫 번째 파라미터는 헤더 네임인데 스페이스 구분자로 구분, strategy는 뭘 남겨둘지 정하는 듯.
RouteLocator dedupeRouteLocator = builder.routes()
.route(p -> p
.path("/**")
.filters(f -> f
.addResponseHeader("Access-Control-Allow-Origin", "*")
// .dedupeResponseHeader("Access-Control-Allow-Credentials Access-Control-Allow-Origin", "RETAIN_FIRST")
)
.uri(testUrl)
)
.build();
// rewritePath는 path를 아예 바꿔주는 역할. http://localhost:8100/post 호출 시 get method로 호출해도 "405 Method Not Allowed" 안나오고 성공.
RouteLocator rewriteRouteLocator = builder.routes().route(p -> p
.path("/**")
.filters(f -> f
.rewritePath("/post", "/get")
)
.uri(testUrl)).build();
// fallback url을 외부 url로 설정
RouteLocator extFallbackRouteLocator = builder.routes()
.route("normal", p -> p
.path("/delay/3")
.filters(f -> f.circuitBreaker(config -> config
.setName("mycmd")
.setFallbackUri("forward:/fallback2")
)
.fallbackHeaders(config -> config.setExecutionExceptionTypeHeaderName("Test-Header"))
.mapRequestHeader("Blue", "X-Request-Red")
)
.uri(testUrl)
)
.route("fallback", p -> p
.path("/fallback2")
.uri("https://www.naver.com")
)
.build();
// path 앞에 고정 값을 붙여 주는 것이라고 하는데 잘 테스트가 안됨. /hello를 호출하게 되면 /mypath/hello로 변경해서 route 해 줌.
RouteLocator prefixRouteLocator = builder.routes()
.route("prefixpath_route", p -> p
.path("/**")
.filters(f -> f.prefixPath("/mypath"))
.uri(testUrl)
)
.build();
// 300번대 status code를 주고 url로 이동 시킨다. 200번대로 하니 이동 안됨.
URL url = new URL("https://www.daum.net");
RouteLocator redirectRouteLocator = builder.routes()
.route(p -> p
.path("/**")
.filters(f -> f.redirect(HttpStatus.FOUND, url))
.uri(testUrl)
)
.build();
// 특정 request, response header를 제거하고 전달...
RouteLocator removeRequestHeaderRouteLocator = builder.routes()
.route(p -> p
.path("/headers")
.filters(f -> f
.removeRequestHeader("X-Request-Foo")
.removeResponseHeader("Server"))
.uri(testUrl)
)
.build();
// 파라미터 제거하고 전달. 아래는 foo라는 파라미터를 제거하고 전달
RouteLocator removeRequestParamrouteLocator = builder.routes()
.route(p -> p
.path("/get")
.filters(f -> f
.removeRequestParameter("foo")
)
.uri(testUrl)
)
.build();
// rewrite 된 곳의 response 된 헤더 값을 변경
RouteLocator rewriteResponseHeaderRouteLocator = builder.routes()
.route(p -> p
.path("/rewrite-response-header")
.filters(f -> f
.rewritePath("/rewrite-response-header", "/headers")
.rewriteResponseHeader("server", "gunicorn/19.9.0", "gunicorn/20.9.0")
)
.uri(testUrl)
)
.build();
// 매뉴얼 상 WebSession::save 를 하는 거라고 하는데 정확한 의미는 모르겠음. GW 이전에서 넘어오는 세션을 유지시켜 준다는 뜻인가?
// Spring Security를 쓰게 된다면 중요하다고 함.
RouteLocator saveSessionRouteLocator = builder.routes()
.route(p -> p
.path("/**")
// .filters(f -> f.saveSession())
.filters(GatewayFilterSpec::saveSession)
.uri(testUrl)
)
.build();
// 보안과 관련된 헤더 조작을 하는 것으로 보이는데 정확한 사용법은 모르겠다.
RouteLocator secureHeadersRouteLocator = builder.routes()
.route(p -> p
.path("/**")
.filters(f -> f
.secureHeaders(
config -> config.withDefaults(secureHeadersProperties)
)
)
.uri(testUrl)
)
.build();
// SetPath 경로를 바꿔 준다. rewrite와 다른게 무엇?
RouteLocator setPathRouteLocator = builder.routes()
.route(p -> p
.path("/red/{segment}")
.filters(f -> f
.setPath("/{segment}")
)
.uri(testUrl)
)
.build();
// setRequestHeader... path 또는 host 정보와 같은 것을 application에서 사용하고자 할 경우 헤더에 셋팅해서 사용.
RouteLocator setRequestHeaderRouteLocator = builder.routes()
.route(p -> p
.path("/**")
.filters(f -> f
.setRequestHeader("X-Request-Red", "Blue")
.setResponseHeader("X-Request-Red", "Blue")
)
.uri(testUrl)
)
.build();
// 응답 http status 코드 값을 변경하여 내려준다.
RouteLocator setStatusRouteLocator = builder.routes()
.route(p -> p
.path("/**")
.filters(f -> f
.setStatus(HttpStatus.UNAUTHORIZED)
)
.uri(testUrl)
)
.build();
// Retry
RouteLocator retryRouteLocator = builder.routes()
.route(p -> p
.path("/**")
.filters(f -> f
.retry(config -> config
.setRetries(3)
.setStatuses(HttpStatus.SERVICE_UNAVAILABLE, HttpStatus.NOT_FOUND)
.setMethods(HttpMethod.GET, HttpMethod.POST)
.setSeries(HttpStatus.Series.CLIENT_ERROR, HttpStatus.Series.SERVER_ERROR)
.setBackoff(new RetryGatewayFilterFactory.BackoffConfig(Duration.ofMillis(10), Duration.ofMillis(50), 2, false))
)
)
.uri("https://www.naver.com/get")
)
.build();
// requestSize filter (max size limit)
RouteLocator requestSizeRouteLocator = builder.routes()
.route(p -> p
.path("/**")
.filters(f -> f
.setRequestSize(DataSize.ofBytes(10))
)
.uri(testUrl)
)
.build();
// setRequestHostHeader
RouteLocator setRequestHostHeaderRouteLocator = builder.routes()
.route(p -> p
.path("/headers")
.filters(f -> f
.setHostHeader("example.org")
)
.uri(testUrl)
)
.build();
return headerAddRouteLocator;
}
자세한 내용은 주석 참고
Between
- after, before를 같이 쓰기 위해서는 between을 사용할 수 있음.
dedupeResponseHeader
- dedupe 라는 영어 단어를 찾아도 알 수 있는 단어가 나오지 않음.
- dedupeResponseHeader method의 javadoc을 근거로 보면 dedupeResponseHeader 메소드의 첫 번째 파라미터에는 중복을 없애고 싶은 헤더 이름을, 두 번째 파라미터에는 중복 제거 전략을 명시하는 것으로 보임.
- 첫 번째 파라미터는 스페이스 구분자를 사용해서 이어서 쓸 수 있음.
- dedupeResponseHeader 주석을 하고 호출하게 되면 아래와 같이 나옴.
- 주석을 풀고 호출하면 중복이 제거 되어서 나옴
- 두 번째 파라미터인 strategy에 쓸 수 있는 값은 RETAIN_FIRST, RETAIN_LAST, RETAIN_UNIQUE 중 하나를 쓸 수 있으며 String을 값을 받음. 내부 적으로는 enum 값을 아래와 같이 셋팅해서 사용 중
org.springframework.cloud.gateway.filter.factory.DedupeResponseHeaderGatewayFilterFactory.java
public enum Strategy {
/**
* Default: Retain the first value only.
*/
RETAIN_FIRST,
/**
* Retain the last value only.
*/
RETAIN_LAST,
/**
* Retain all unique values in the order of their first encounter.
*/
RETAIN_UNIQUE
}
fallbackHeaders
- fallback을 설정할 때 fallbackHeaders를 설정하게 되는 경우는 정확히 테스트 못 해 봤지만, 매뉴얼 상 Exception 내용을 헤더에 담아 보내주는 것으로 보인다. - docs.spring.io/spring-cloud-gateway/docs/2.2.6.BUILD-SNAPSHOT/reference/html/#fallback-headers 참고
- 기본 값은 위 매뉴얼에 나와 있는 것 처럼 아래의 내용으로 전달 되는 듯
- executionExceptionTypeHeaderName ("Execution-Exception-Type")
- executionExceptionMessageHeaderName ("Execution-Exception-Message")
- rootCauseExceptionTypeHeaderName ("Root-Cause-Exception-Type")
- rootCauseExceptionMessageHeaderName ("Root-Cause-Exception-Message")
- 기본 값은 위 매뉴얼에 나와 있는 것 처럼 아래의 내용으로 전달 되는 듯
mapRequestHeader
- mapRequestHeader는 From으로 받은 헤더를 To로 전달해 주는 것으로 보임.
secureHeaders
- secureHeaders 관련해서는 보안과 관련된 헤더를 덧붙여 주는 것으로 보인다. (secure header와 관련된 내용은 blog.appcanary.com/2017/http-security-headers.html 참고)
- @Autowired 시킨 "private SecureHeadersProperties secureHeadersProperties" 값을 "secureHeaders(config -> config.withDefaults(secureHeadersProperties))" 해 주게 되면 response header에 보안과 관련된 헤더가 붙어 나오게 된다.
- withDefaults는 org.springframework.cloud.gateway.filter.factory에 위치해 있다.
- 아래는 보안과 관련된 헤더를 적용하지 않은 경우 http://httpbin.org 로부터 받게 된 헤더 정보이다.
- 반면에 secureHeaders를 기본 Properties로 적용시키게 되면 아래와 같이 나오게 된다.
- 이와 관련하여 자신의 환경에 맞게 커스터마이징을 하려면 각 헤더별로 set을 하면 된다.
- 기본값에 Set 한 SecureHeadersProperties 는 org.springframework.cloud.gateway.filter.factory에 위치한 SecureHeadersProperties 클래스이며 "@ConfigurationProperties("spring.cloud.gateway.filter.secure-headers")"로 정의 되어져 있다.
- 필요한 헤더값이 궁금하면 들어가서 살펴볼 수 있다.
setStatus
- response의 헤더 값을 변경하여 내려보내줄 수 있다.
- filters(f -> f.setStatus(HttpStatus.UNAUTHORIZED)) 와 같이 하게 될 경우 401 status 값을 리턴한다.
- 원래의 http status 값을 헤더에 포함시켜 내려 주고 싶으면 yml 파일에 다음과 같이 적으면 된다.
spring:
cloud:
gateway:
set-status:
original-status-header-name: original-http-status
위와 같이 설정을 하게 될 경우에는 다음과 같은 모습으로 헤더가 내려온다.
Retry
- retry 하는 경우를 정의하게 되면 설정 값에 따라 재시도를 하게 된다.
- setRetries(3) : 3회 재시도 한다
- setStatuses(HttpStatus.SERVICE_UNAVAILABLE) : http status가 SERVICE_UNAVAILABLE(503)인 경우 재시도 한다.
- HttpStatus는 org.springframework.http를 사용한다.
- setMethods(HttpMethod.GET, HttpMethod.POST) : 해당 메소드를 사용하는 경우
- setSeries(HttpStatus.Series.CLIENT_ERROR, HttpStatus.Series.SERVER_ERROR) : HttpStatus 코드를 일일히 명시하기 힘들경우 1xx, 2xx 식으로 정의 가능. 해당 값은 org.springframework.http.HttpStatus 안에 enum으로 정의 된 Series를 사용
- INFORMATIONAL(1)
- SUCCESSFUL(2)
- REDIRECTION(3)
- CLIENT_ERROR(4)
- SERVER_ERROR(5)
- setBackoff(new RetryGatewayFilterFactory.BackoffConfig(Duration.ofMillis(10), Duration.ofMillis(50), 2, false))
- 매뉴얼 상 retry를 지수로 감소시키는 것을 설정하는 것으로 보임
- 파라미터로 넘기는 것은 정적 클래스인 RetryGatewayFilterFactory.BackoffConfig 이며 각 파라미터는 다음과 같음
- 소스 예제처럼 셋팅할 경우 처음 10밀리 세컨드로 재시도 하고 그 이후 50밀리 세컨드가 될 때까지 지수 형태로 천천히 재시도 하는 것으로 보임. 매뉴얼 참고 : docs.spring.io/spring-cloud-gateway/docs/2.2.6.BUILD-SNAPSHOT/reference/html/#the-retry-gatewayfilter-factory
- 실제 Get, CLIENT_ERROR 으로 설정한 상태에서 없는 URL로 route를 하게 되면 로컬 컴퓨터 기존 250ms 걸리던 호출이 retry로 인해 1.5 ~ 2초 이상 걸리는 것을 볼 수 있음
- log를 보면 아래와 같이 재시도 하는 모습을 볼 수 있다. 404 not found도 재시도 하도록 해 놔서 아래와 같이 테스트가 가능하다.
RequestSize
- 파일 업로드 시 파일 사이즈의 제한을 줄 수 있다.
- Postman으로 다음과 같이 테스트 해 보면 HttpStatus code 413이 내려오고 헤더 정보에 에러 메시지(Request size is larger than permissible limit. Request size is 701.3 kB where permissible limit is 10 B)가 아래와 같이 내려 온다.
- 선언하지 않을 경우 기본 용량은 5MB 인 것으로 보임 (The default request size is set to five MB if not provided as a filter argument in the route definition.)
내용이 길어져서 Global Filter 부분 부터는 나눠서 기록한다.
728x90
반응형
'Architecture > MSA' 카테고리의 다른 글
Spring Cloud Gateway - Custom Predicate with AbstractRoutePredicateFactory (0) | 2021.01.06 |
---|---|
Spring Cloud Gateway - Global Filter (0) | 2021.01.06 |
Hystrix, Feign을 이용한 개발 (0) | 2020.12.18 |
Zuul을 이용한 Gateway 구축 시 설정 (0) | 2020.12.11 |
MSA 관련 북마크 (0) | 2020.11.20 |