之前的几篇文章中,我们已经提到了如何使用SpringCloud Gateway,那几篇文章的内容已经足够做普通项目使用了,但是如果你想深入了解这个东西,或者说是看完这篇文章你用起来跟普通人就完全不是一个等级的了
路由谓语工厂的骚操作
AfterRoutePredicateFactory
前置时间路由工厂,匹配当前时间发生后的请求
spring:
cloud:
gateway:
routes:
- id: after_route
uri: http://example.org
predicates:
- After=2017-01-20T17:42:47.789-07:00[America/Denver]
2
3
4
5
6
7
8
BeforeRoutePredicateFactory
后置时间路由工厂,匹配当前时间发生之前的请求
spring:
cloud:
gateway:
routes:
- id: before_route
uri: http://example.org
predicates:
- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
2
3
4
5
6
7
8
BetweenRoutePredicateFactory
匹配时间段的路由谓语工厂
spring:
cloud:
gateway:
routes:
- id: between_route
uri: http://example.org
predicates:
- Betweeen=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
2
3
4
5
6
7
8
这个谓语工厂的判断逻辑如下:
public Predicate<ServerWebExchange> apply(Config config) {
ZonedDateTime datetime1 = getZonedDateTime(config.datetime1);
ZonedDateTime datetime2 = getZonedDateTime(config.datetime2);
Assert.isTrue(datetime1.isBefore(datetime2),
config.datetime1 +
" must be before " + config.datetime2);
return exchange -> {
final ZonedDateTime now = ZonedDateTime.now();
return now.isAfter(datetime1) && now.isBefore(datetime2);
};
}
2
3
4
5
6
7
8
9
10
11
CookieRoutePredicateFactory
匹配请求cookie的路由谓语工厂
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://example.org
predicates:
- Cookie=chocolate, ch.p
2
3
4
5
6
7
8
判断逻辑如下:
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
List<HttpCookie> cookies = exchange.getRequest().getCookies().get(config.name);
if (cookies == null) {
return false;
}
for (HttpCookie cookie : cookies) {
if (cookie.getValue().matches(config.regexp)) {
return true;
}
}
return false;
};
}
2
3
4
5
6
7
8
9
10
11
12
13
14
HeaderRoutePredicateFactory
匹配请求header的路由谓语工厂
spring:
cloud:
gateway:
routes:
- id: header_route
uri: http://example.org
predicates:
- Header=X-Request-Id, \d+
2
3
4
5
6
7
8
实现逻辑如下:
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
List<String> values = exchange.getRequest().getHeaders().get(config.header);
if (values != null) {
for (String value : values) {
if (value.matches(config.regexp)) {
return true;
}
}
}
return false;
};
}
2
3
4
5
6
7
8
9
10
11
12
13
HostRoutePredicateFactory
匹配请求host的路由谓语工厂
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://example.org
predicates:
- Host=**.somehost.org
2
3
4
5
6
7
8
实现逻辑如下:
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
String host = exchange.getRequest().getHeaders().getFirst("Host");
return this.pathMatcher.match(config.getPattern(), host);
};
}
2
3
4
5
6
MethodRoutePredicateFactory
匹配请求method的路由谓语工厂
spring:
cloud:
gateway:
routes:
- id: method_route
uri: http://example.org
predicates:
- Method=GET
2
3
4
5
6
7
8
实现逻辑如下:
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
HttpMethod requestMethod = exchange.getRequest().getMethod();
return requestMethod == config.getMethod();
};
}
2
3
4
5
6
PathRoutePredicateFactory
匹配请求路径的路由谓语工厂
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://example.org
predicates:
- Path=/foo/{segment}
2
3
4
5
6
7
8
实现逻辑如下:
public Predicate<ServerWebExchange> apply(Config config) {
synchronized (this.pathPatternParser) {
config.pathPattern = this.pathPatternParser.parse(config.pattern);
}
return exchange -> {
PathContainer path = parsePath(exchange.getRequest().getURI().getPath());
boolean match = config.pathPattern.matches(path);
traceMatch("Pattern", config.pathPattern.getPatternString(), path, match);
if (match) {
PathMatchInfo uriTemplateVariables = config.pathPattern.matchAndExtract(path);
exchange.getAttributes().put(URI_TEMPLATE_VARIABLES_ATTRIBUTE, uriTemplateVariables);
return true;
} else {
return false;
}
};
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
QueryRoutePredicateFactory
匹配请求参数的路由谓语工厂
spring:
cloud:
gateway:
routes:
- id: query_route
uri: http://example.org
predicates:
- Query=foo, ba.
2
3
4
5
6
7
8
实现逻辑如下:
public Predicate<ServerWebExchange> apply(Config config) {
return exchange -> {
if (!StringUtils.hasText(config.regexp)) {
// check existence of header
return exchange.getRequest().getQueryParams().containsKey(config.param);
}
List<String> values = exchange.getRequest().getQueryParams().get(config.param);
for (String value : values) {
if (value.matches(config.regexp)) {
return true;
}
}
return false;
};
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
RemoteAddrRoutePredicateFactory
匹配远程地址的路由谓语工厂
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: http://example.org
predicates:
- RemoteAddr=192.168.1.1/24
2
3
4
5
6
7
8
实现逻辑如下:
public Predicate<ServerWebExchange> apply(Config config) {
List<IpSubnetFilterRule> sources = convert(config.sources);
return exchange -> {
InetSocketAddress remoteAddress = config.remoteAddressResolver.resolve(exchange);
if (remoteAddress != null) {
String hostAddress = remoteAddress.getAddress().getHostAddress();
String host = exchange.getRequest().getURI().getHost();
if (log.isDebugEnabled() && !hostAddress.equals(host)) {
log.debug("Remote addresses didn't match " + hostAddress + " != " + host);
}
for (IpSubnetFilterRule source : sources) {
if (source.matches(remoteAddress)) {
return true;
}
}
}
return false;
};
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
拦截器的骚操作
AddRequestHeaderGatewayFilterFactory
为请求中添加header的拦截器
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: http://example.org
filters:
- AddRequestHeader=X-Request-Foo, Bar
2
3
4
5
6
7
8
实现逻辑如下:
public GatewayFilter apply(NameValueConfig config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest().mutate()
.header(config.getName(), config.getValue())
.build();
return chain.filter(exchange.mutate().request(request).build());
};
}
2
3
4
5
6
7
8
9
AddRequestParameterGatewayFilterFactory
为请求中添加请求参数的拦截器
spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: http://example.org
filters:
- AddRequestParameter=foo, bar
2
3
4
5
6
7
8
实现逻辑如下:
public GatewayFilter apply(NameValueConfig config) {
return (exchange, chain) -> {
URI uri = exchange.getRequest().getURI();
StringBuilder query = new StringBuilder();
String originalQuery = uri.getRawQuery();
if (StringUtils.hasText(originalQuery)) {
query.append(originalQuery);
if (originalQuery.charAt(originalQuery.length() - 1) != '&') {
query.append('&');
}
}
//TODO urlencode?
query.append(config.getName());
query.append('=');
query.append(config.getValue());
try {
URI newUri = UriComponentsBuilder.fromUri(uri)
.replaceQuery(query.toString())
.build(true)
.toUri();
ServerHttpRequest request = exchange.getRequest().mutate().uri(newUri).build();
return chain.filter(exchange.mutate().request(request).build());
} catch (RuntimeException ex) {
throw new IllegalStateException("Invalid URI query: \"" + query.toString() + "\"");
}
};
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
AddResponseHeaderGatewayFilterFactory
为响应中添加header的拦截器
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: http://example.org
filters:
- AddResponseHeader=X-Response-Foo, Bar
2
3
4
5
6
7
8
实现逻辑如下:
public GatewayFilter apply(NameValueConfig config) {
return (exchange, chain) -> {
exchange.getResponse().getHeaders().add(config.getName(), config.getValue());
return chain.filter(exchange);
};
}
2
3
4
5
6
7
HystrixGatewayFilterFactory
服务熔断的拦截器
spring:
cloud:
gateway:
routes:
- id: hytstrix_route
uri: http://example.org
filters:
- Hystrix=myCommandName
args:
name: default
fallbackUri: forward:/defaultfallback
2
3
4
5
6
7
8
9
10
11
实现逻辑如下,建议搭配之前的Hystrix源码解析阅读
public GatewayFilter apply(Config config) {
//TODO: if no name is supplied, generate one from command id (useful for default filter)
if (config.setter == null) {
Assert.notNull(config.name, "A name must be supplied for the Hystrix Command Key");
HystrixCommandGroupKey groupKey = HystrixCommandGroupKey.Factory.asKey(getClass().getSimpleName());
HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey(config.name);
config.setter = Setter.withGroupKey(groupKey)
.andCommandKey(commandKey);
}
return (exchange, chain) -> {
RouteHystrixCommand command = new RouteHystrixCommand(config.setter, config.fallbackUri, exchange, chain);
return Mono.create(s -> {
Subscription sub = command.toObservable().subscribe(s::success, s::error, s::success);
s.onCancel(sub::unsubscribe);
}).onErrorResume((Function<Throwable, Mono<Void>>) throwable -> {
if (throwable instanceof HystrixRuntimeException) {
HystrixRuntimeException e = (HystrixRuntimeException) throwable;
if (e.getFailureType() == TIMEOUT) { //TODO: optionally set status
setResponseStatus(exchange, HttpStatus.GATEWAY_TIMEOUT);
return exchange.getResponse().setComplete();
}
}
return Mono.error(throwable);
}).then();
};
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
PrefixPathGatewayFilterFactory
为请求路径添加前缀的拦截器
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: http://example.org
filters:
- PrefixPath=/mypath
2
3
4
5
6
7
8
实现逻辑如下:
public GatewayFilter apply(Config config) {
return (exchange, chain) -> {
boolean alreadyPrefixed = exchange.getAttributeOrDefault(GATEWAY_ALREADY_PREFIXED_ATTR, false);
if (alreadyPrefixed) {
return chain.filter(exchange);
}
exchange.getAttributes().put(GATEWAY_ALREADY_PREFIXED_ATTR, true);
ServerHttpRequest req = exchange.getRequest();
addOriginalRequestUrl(exchange, req.getURI());
String newPath = config.prefix + req.getURI().getRawPath();
ServerHttpRequest request = req.mutate()
.path(newPath)
.build();
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, request.getURI());
if (log.isTraceEnabled()) {
log.trace("Prefixed URI with: "+config.prefix+" -> "+request.getURI());
}
return chain.filter(exchange.mutate().request(request).build());
};
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
RequestRateLimiterGatewayFilterFactory
请求限流的拦截器
这个拦截器相比于上方的各个拦截器还是稍微复杂一点的。 首先需要引入一个额外的包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
2
3
4
然后呢你要基于SpringBoot做好redis的自动装配,紧接着就是设置限流key,限流key的设置需要实现keyResolver
接口,然后通过它的这个方法 resolve(ServerWebExchange exchange)返回你需要设置的限流key
配置的地方是这样的
spring:
cloud:
gateway:
routes:
- id: requestratelimiter_route
uri: http://example.org
filters:
- RequestRateLimiter=10, 20, #{@userKeyResolver}
2
3
4
5
6
7
8
其中前两个参数分别是令牌桶算法的两个参数,填充速率和限流阈值,更多关于限流算法的操作可以参考我的这篇文章:大型网站限流算法的实现和改造
拦截器实现逻辑如下:
public GatewayFilter apply(Config config) {
KeyResolver resolver = (config.keyResolver == null) ? defaultKeyResolver : config.keyResolver;
RateLimiter<Object> limiter = (config.rateLimiter == null) ? defaultRateLimiter : config.rateLimiter;
return (exchange, chain) -> {
Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
return resolver.resolve(exchange).flatMap(key ->
// TODO: if key is empty?
limiter.isAllowed(route.getId(), key).flatMap(response -> {
for (Map.Entry<String, String> header : response.getHeaders().entrySet()) {
exchange.getResponse().getHeaders().add(header.getKey(), header.getValue());
}
if (response.isAllowed()) {
return chain.filter(exchange);
}
exchange.getResponse().setStatusCode(config.getStatusCode());
return exchange.getResponse().setComplete();
}));
};
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
RedirectToGatewayFilterFactory
请求重定向的拦截器
spring:
cloud:
gateway:
routes:
- id: prefixpath_route
uri: http://example.org
filters:
- RedirectTo=302, http://acme.org
2
3
4
5
6
7
8
实现逻辑如下:
public GatewayFilter apply(String statusString, String urlString) {
final HttpStatus httpStatus = parse(statusString);
Assert.isTrue(httpStatus.is3xxRedirection(), "status must be a 3xx code, but was " + statusString);
final URL url;
try {
url = URI.create(urlString).toURL();
} catch (MalformedURLException e) {
throw new IllegalArgumentException("Invalid url " + urlString, e);
}
return apply(httpStatus, url);
}
2
3
4
5
6
7
8
9
10
11
RemoveRequestHeaderGatewayFilterFactory/RemoveResponseHeaderGatewayFilterFactory
删除请求、响应中的header拦截器
spring:
cloud:
gateway:
routes:
- id: removerequestheader_route
uri: http://example.org
filters:
- RemoveRequestHeader=X-Request-Foo
2
3
4
5
6
7
8
实现逻辑这里就贴其中一个了:
public GatewayFilter apply(NameConfig config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest().mutate()
.headers(httpHeaders -> httpHeaders.remove(config.getName()))
.build();
return chain.filter(exchange.mutate().request(request).build());
};
}
2
3
4
5
6
7
8
9
RewritePathGatewayFilterFactory
重写请求路径的拦截器
spring:
cloud:
gateway:
routes:
- id: rewritepath_route
uri: http://example.org
predicates:
- Path=/foo/**
filters:
- RewritePath=/foo/
2
3
4
5
6
7
8
9
10
实现逻辑如下:
public GatewayFilter apply(Config config) {
String replacement = config.replacement.replace("$\\", "$");
return (exchange, chain) -> {
ServerHttpRequest req = exchange.getRequest();
addOriginalRequestUrl(exchange, req.getURI());
String path = req.getURI().getRawPath();
String newPath = path.replaceAll(config.regexp, replacement);
ServerHttpRequest request = req.mutate()
.path(newPath)
.build();
exchange.getAttributes().put(GATEWAY_REQUEST_URL_ATTR, request.getURI());
return chain.filter(exchange.mutate().request(request).build());
};
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17