博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
SpringCloud-Gateway之RoutePredicateFactory
阅读量:4164 次
发布时间:2019-05-26

本文共 46486 字,大约阅读时间需要 154 分钟。

转载自:https://blog.csdn.net/weixin_44100910/article/details/106439122

前言:

本篇介绍SpringCloud的网关模块的RoutePredicateFactory,通过提供的RoutePredicateFactory来实现配置不同的路由规则。目前支持12种RoutePredicateFactory,简介如下:

  • QueryRoutePredicateFactory:根据query参数路由

  • PathRoutePredicateFactory:根据Path路由

  • AfterRoutePredicateFactory:根据请求的时间路由,在指定时间之后

  • BeforeRoutePredicateFactory:根据请求的时间路由,在指定时间之前

  • BetweenRoutePredicateFactory:根据请求的时间路由,在指定时间范围内

  • CookieRoutePredicateFactory:根据请求头中的Cookie路由

  • HeaderRoutePredicateFactory:根据请求头中的值路由

  • HostRoutePredicateFactory:根据请求头中的Host路由

  • MethodRoutePredicateFactory:根据请求Method路由

  • RemoteAddrRoutePredicateFactory:根据请求的远程IP路由

  • WeightRoutePredicateFactory:根据权重路由

  • ReadBodyPredicateFactory :暂未官方文档说明

 

1、QueryRoutePredicateFactory

根据查询的参数路由,配置关键词:Query,示例如下:

spring:     
cloud:
gateway:
routes:
- id: query_hello # 自定义的路由ID
uri: forward:/hello # 跳转的路径,这里forward协议是跳转到当前的服务的API
predicates:
- Query=q, hello # 路由规则,根据查询条件,需要满足{url}?q=hello
  • Query={key}:只需要满足query参数中存在key即可

  • Query={key}, {value},需要满足query的参数中存在key的参数值必须为value

  • 其中,key不支持正则表达式,value支持正则表达式

源码:

public boolean test(ServerWebExchange exchange) {     
//如果没有配置{value},则只需要判断{key}是否存在即可
if (!StringUtils.hasText(config.regexp)) {
return exchange.getRequest().getQueryParams()
.containsKey(config.param);
}
//校验{value}是否匹配
List
values = exchange.getRequest().getQueryParams()
.get(config.param);
if (values ==
null) {
return
false;
}
for (String value : values) {
if (value !=
null && value.matches(config.regexp)) {
return
true;
}
}
return
false;
}

2、PathRoutePredicateFactory

根据请求Path路由,配置关键词:Path,示例如下:

spring:     
cloud:
gateway:
routes:
- id: path_baidu
uri: https:
//www.baidu.com
predicates:
- Path=/baidu
- id: path_var
uri: forward:/hello?
var={ziya}
predicates:
- Path=/
var/{ziya}
filters:
- AddRequestParameter=ziya, zy-{ziya} # 在AddRequestParameter Filter中引用URI模板变量:ziya
- id: path_regex
uri: forward:/hello
predicates:
- Path=/regex
/**

支持的配置格式:

  • Path=/baidu:固定路径

  • Path=/var/{ziya}:支持定义URL模板变量,该变量可以在自定义的GatewayFilter中使用,也可以结合springcloud-gateway自带的GatewayFilter使用,在配置文件中使用该变量

//在自定义的GatewayFilter中读取URL模板变量:hello     
String value = ServerWebExchangeUtils.getUriTemplateVariables(exchange).get(
"hello");
  • Path=/regex/**:支持模糊匹配的路径,该配置将/regex下的所有请求转到指定的uri。

源码:

//根据配置的路径创建Pattern     
synchronized (
this.pathPatternParser) {
pathPatternParser.setMatchOptionalTrailingSeparator(
config.isMatchOptionalTrailingSeparator());
config.getPatterns().forEach(pattern -> {
PathPattern pathPattern =
this.pathPatternParser.parse(pattern);
pathPatterns.add(pathPattern);
});
}
public boolean test(ServerWebExchange exchange) {
//获得Path
PathContainer path = parsePath(
exchange.getRequest().getURI().getRawPath());
//用编译好的Patterns来匹配Path
Optional
optionalPathPattern = pathPatterns.stream()
.filter(pattern -> pattern.matches(path)).findFirst();
//如果匹配到,将URI中的模板参数提取出来,放到Exchange中
if (optionalPathPattern.isPresent()) {
PathPattern pathPattern = optionalPathPattern.get();
traceMatch(
"Pattern", pathPattern.getPatternString(), path,
true);
PathMatchInfo pathMatchInfo = pathPattern.matchAndExtract(path);
putUriTemplateVariables(exchange, pathMatchInfo.getUriVariables());
return
true;
}
else {
traceMatch(
"Pattern", config.getPatterns(), path,
false);
return
false;
}
}

3、AfterRoutePredicateFactory

根据请求时间路由,配置关键词:After,示例如下:

spring:     
cloud:
gateway:
routes:
- id: after_world
uri: forward:/world
predicates:
- After=
2020-
05-
28T22:
10:
00.
000+
08:
00[Asia/Shanghai]
- id: after_baidu
uri: https:
//www.baidu.com
predicates:
- After=
2020-
05-
28T22:
00:
00.
000+
08:
00[Asia/Shanghai]

当请求的时间在指定时间之后,就会将请求路由到指定的uri上

注意:

  • 如果有多个满足条件的after配置,则会路由到第一个uri。

  • 参数格式,满足ZonedDateTime格式,

        如:2020-05-28T22:00:00.000+08:00[Asia/Shanghai]

源码:

public boolean test(ServerWebExchange serverWebExchange) {     
final ZonedDateTime now = ZonedDateTime.now();
return now.isAfter(config.getDatetime());
}

4、BeforeRoutePredicateFactory

根据请求时间路由,配置关键词:Before,示例如下:

spring:     
cloud:
gateway:
routes:
- id: before_hello
uri: forward:/hello
predicates:
- Before=
2020-
05-
29T22:
01:
00.
000+
08:
00[Asia/Shanghai]
- id: before_world
uri: forward:/world
predicates:
- Before=
2020-
05-
29T22:
00:
00.
000+
08:
00[Asia/Shanghai]

当请求的时间在指定时间之前,将会将请求路由到指定的uri上

注意(同after):

  • 如果有多个满足条件的before配置,则会路由到第一个uri。

  • 参数格式,满足ZonedDateTime格式,

    如:2020-05-28T22:00:00.000+08:00[Asia/Shanghai]

源码:

public boolean test(ServerWebExchange serverWebExchange) {     
final ZonedDateTime now = ZonedDateTime.now();
return now.isBefore(config.getDatetime());
}

5、BetweenRoutePredicateFactory

根据请求时间路由,配置关键词:Between,示例如下:

spring:     
cloud:
gateway:
routes:
- id: before_world
uri: forward:/world
predicates:
- Between=
2020-
05-
29T10:
00:
00.
000+
08:
00[Asia/Shanghai],
2020-
05-
29T11:
00:
00.
000+
08:
00[Asia/Shanghai]
- id: before_hello
uri: forward:/hello
predicates:
- Between=
2020-
05-
29T10:
40:
00.
000+
08:
00[Asia/Shanghai],
2020-
05-
29T10:
55:
00.
000+
08:
00[Asia/Shanghai]

当请求的时间在指定时间范围内,将会将请求路由到指定的uri上

注意(同after):

  • 如果有多个满足条件的between配置,则会路由到第一个uri。

  • 参数格式,满足ZonedDateTime格式,

    如:2020-05-28T22:00:00.000+08:00[Asia/Shanghai]

  • 如果同时存在:before,after,between配置,会路由到第一个满足条件的uri

源码:

public boolean test(ServerWebExchange serverWebExchange) {     
final ZonedDateTime now = ZonedDateTime.now();
return now.isAfter(config.getDatetime1())
&& now.isBefore(config.getDatetime2());
}

6、CookieRoutePredicateFactory

根据请求头中的Cookie路由,配置关键词:Cookie,示例如下:

spring:     
cloud:
gateway:
routes:
- id: cookie_hello2
uri: forward:/hello
filters:
- AddRequestParameter=
var, cookie-regexp
predicates:
- Cookie=X-key, v.*
- id: cookie_hello
uri: forward:/hello
filters:
- AddRequestParameter=
var, cookie-no-value
predicates:
- Cookie=X-key, value

    当请求中存在X-key=value的Cookie,则将请求路由到指定的uri,如果有多个满足,则路由到第一个匹配的URI。匹配规则如下:

  • Cookie=X-key, value:指定Cookie的值

  • Cookie=X-key, v.*:正则表达式匹配Cookie的值

源码:

public boolean test(ServerWebExchange exchange) {     
List
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;
}

7、HeaderRoutePredicateFactory

根据请求头中的值路由,配置关键词:Header,示例如下:

spring:     
cloud:
gateway:
routes:
- id: header_hello2
uri: forward:/hello
filters:
- AddRequestParameter=
var, header-value-zy
predicates:
- Header=X-zy, v-zy
- id: header_hello1
uri: forward:/hello
filters:
- AddRequestParameter=
var, header-no-value
predicates:
- Header=X-zy

当请求中存在X-key=value的Header,则将请求路由到指定的uri,如果有多个满足,则路由到第一个匹配的URI。配置Value时,支持正则表达式。

 注意:如果需要按先匹配指定值优先,然后再匹配Header名,则需要将指定Header值的路由配置在前面。

  源码:

//是否配置了Header值     
boolean hasRegex = !StringUtils.isEmpty(config.regexp);
public boolean test(ServerWebExchange exchange) {
List
values = exchange.getRequest().getHeaders()
.getOrDefault(config.header, Collections.emptyList());
//如果不存在指定的Header名,返回不匹配
if (values.isEmpty()) {
return
false;
}
//如果指定了Header值
if (hasRegex) {
// 通过正则表达式匹配
return values.stream()
.anyMatch(value -> value.matches(config.regexp));
}
//如果配置的时没有指定Header值,则不用判断值,直接匹配
return
true;
}

8、HostRoutePredicateFactory

根据请求头中的Host路由,配置关键词:Host,示例如下

spring:     
cloud:
gateway:
routes:
- id: host-hello-dev
uri: forward:/hello
filters:
- AddRequestParameter=
var, dev
predicates:
- Host=dev.wx.com
- id: host-hello-multi
uri: forward:/hello
filters:
- AddRequestParameter=
var, multi
predicates:
- Host=dev.wx.com, qa.wx.com
- id: host-hello-multi-
var
uri: forward:/hello
filters:
- AddRequestParameter=
var, {env}
predicates:
- Host={env}.wx.com, {env}.bd.com
- id: host-hello-star
uri: forward:/hello
filters:
- AddRequestParameter=
var, env-star
predicates:
- Host=**.wx.com
- id: host-hello-env
uri: forward:/hello
filters:
- AddRequestParameter=
var, env-{env}
predicates:
- Host={env}.wx.com

根据请求头中的Host属性来匹配,如果能匹配到,则将请求路由到指定URI上,如果配置了多个Host,则路由到第一个匹配。匹配规则如下:

  • Host=dev.wx.com:根据固定的Host值匹配

  • Host=**.wx.com:根据通配符匹配

  • Host={env}.wx.com:和通配符效果相同,区别是:可以在自定义的GatewayFilter中通过API获取URI模板变量或者通过配置springcloud-gateway提供的GatewayFilter时引用URI模板变量。

  • Host=dev.wx.com, qa.wx.com:可以配置多个Host来进行匹配

URI模板变量的使用方式:

//通过API获取     
ServerWebExchangeUtils.getUriTemplateVariables(exchange).get(
"env");
//配置文件中配置
filter:
- AddRequestParameter=
var, env-{env}

源码:

public boolean test(ServerWebExchange exchange) {     
//取Header中的第一个host
String host = exchange.getRequest().getHeaders().getFirst(
"Host");
//如果配置了多个pattern,查找第一个匹配的pattern
Optional
optionalPattern = config.getPatterns().stream()
.filter(pattern -> pathMatcher.match(pattern, host)).findFirst();
if (optionalPattern.isPresent()) {
//将URI中的模板变量添加到exchange中,供其他GatewayFilter使用
Map
variables = pathMatcher
.extractUriTemplateVariables(optionalPattern.get(), host);
ServerWebExchangeUtils.putUriTemplateVariables(exchange, variables);
return
true;
}
return
false;
}

 注意:

  • 通过源码发现,要注意的是获取Header中的第一个Host,如果通过代理添加了多个Host,其他的会被忽略。

  • 如果配置:Host=dev.wx.com, {env}.bd.com,而其他地方引用了{env}这个模板变量,当通过dev.wx.com访问时,就会出现500错误,无法获取{env}变量。

9、MethodRoutePredicateFactory

根据请求Method路由,配置关键词:Method,示例如下

spring:     
cloud:
gateway:
routes:
- id: method-post
uri: forward:/hello
filters:
- AddRequestParameter=
var, post
predicates:
- Method=post
- id: method-get
uri: forward:/hello
filters:
- AddRequestParameter=
var, get
predicates:
- Method=get, post

根据请求的Method进行匹配,如果能匹配到,则将请求路由到指定URI上;支持指定多个Method;配置的method不区分大小写;如果有多个匹配的Method,则路由到匹配到的第一个URI。

源码:

public boolean test(ServerWebExchange exchange) {     
HttpMethod requestMethod = exchange.getRequest().getMethod();
return stream(config.getMethods())
.anyMatch(httpMethod -> httpMethod == requestMethod);
}

10、RemoteAddrRoutePredicateFactory

根据请求的远程IP路由,配置关键词:RemoteAddr,示例如下

spring:     
cloud:
gateway:
routes:
- id: remote-addr1
uri: forward:/hello
filters:
- AddRequestParameter=
var, addr1
predicates:
- RemoteAddr=
192.168.
0.100/
24
- id: method-addr2
uri: forward:/hello
filters:
- AddRequestParameter=
var, addr2
predicates:
- RemoteAddr=
192.168.
0.100
- id: method-addr3
uri: forward:/hello
filters:
- AddRequestParameter=
var, addr2
predicates:
- RemoteAddr=
192.168.
0.100,
192.168.
0.101,
192.168.
0.102

根据请求的远程IP进行匹配,如果能匹配到,则将请求路由到指定URI上;配置规则如下:

  • 指定一个固定的IP

  • 指定多个IP,用逗号隔开

  • 如果配置的IP没有指定子网掩码({ip}/{子网掩码}),则默认32(255.255.255.255)

源码:

public boolean test(ServerWebExchange exchange) {     
InetSocketAddress remoteAddress = config.remoteAddressResolver
.resolve(exchange);
if (remoteAddress !=
null && remoteAddress.getAddress() !=
null) {
String hostAddress = remoteAddress.getAddress().getHostAddress();
String host = exchange.getRequest().getURI().getHost();
for (IpSubnetFilterRule source : sources) {
if (source.matches(remoteAddress)) {
return
true;
}
}
}
return
false;
}

11、WeightRoutePredicateFactory

根据权重路由,配置关键词:Weight,示例如下:

spring:     
cloud:
gateway:
routes:
- id: weight-high
uri: forward:/hello
filters:
- AddRequestParameter=
var, high
predicates:
- Weight=group1,
15
- Path=/weight
- id: method-low
uri: forward:/hello
filters:
- AddRequestParameter=
var, low
predicates:
- Weight=group1,
5
- Path=/weight

根据配置的权重来路由,配置权重时需要指定分组,即Weight属性的第一个参数,为分组名,不同group单独计算权重,所以可以配置多个组。权重值为第二个参数,同一组里的权重值的总和 没有要求,最终计算会转换成小于1的double类型。

 源码:

  • WeightCalculatorWebFilter

//项目启动时执行,解析配置的权重,并将整型的权重转换成小于1的double权重,按照从小到大的顺序放入到List中。     
//将最终解析的结果存储在变量groupWeights中
void addWeightConfig(WeightConfig weightConfig) {
  String group = weightConfig.getGroup();
GroupWeightConfig config;
if (groupWeights.containsKey(group)) {
config =
new GroupWeightConfig(groupWeights.get(group));
}
else {
config =
new GroupWeightConfig(group);
}
config.weights.put(weightConfig.getRouteId(), weightConfig.getWeight());
//将权重的int类型转换成double类型
int weightsSum =
0;
for (Integer weight : config.weights.values()) {
weightsSum += weight;
}
final AtomicInteger index =
new AtomicInteger(
0);
for (Map.Entry
entry : config.weights.entrySet()) {
String routeId = entry.getKey();
Integer weight = entry.getValue();
Double nomalizedWeight = weight / (
double) weightsSum;
config.normalizedWeights.put(routeId, nomalizedWeight);
config.rangeIndexes.put(index.getAndIncrement(), routeId);
}
config.ranges.clear()
config.ranges.add(
0.0);
List
values =
new ArrayList<>(config.normalizedWeights.values());
for (
int i =
0; i < values.size(); i++) {
Double currentWeight = values.get(i);
Double previousRange = config.ranges.get(i);
Double range = previousRange + currentWeight;
config.ranges.add(range);
}
// only update after all calculations
groupWeights.put(group, config);
}
//当接收到请求时执行,从变量groupWeights中读取配置,根据生成的随机值,选择routeId,
//再放入到ServerWebExchange的Attributes中,key为:{pre}.routeWeight
public Mono
filter(ServerWebExchange exchange, WebFilterChain chain) {
Map
weights = getWeights(exchange);
for (String group : groupWeights.keySet()) {
GroupWeightConfig config = groupWeights.get(group);
if (config ==
null) {
continue;
// nothing we can do, but this is odd
}
//生成随机变量
double r =
this.random.nextDouble();
List
ranges = config.ranges;
//根据随机值选择RouteId
for (
int i =
0; i < ranges.size() -
1; i++) {
if (r >= ranges.get(i) && r < ranges.get(i +
1)) {
String routeId = config.rangeIndexes.get(i);
weights.put(group, routeId);
break;
}
}
}
return chain.filter(exchange);
}
  • WeightRoutePredicateFactory

//从ServerWebExchange中读取{pre}.routeWeight属性,该属性读取到的就是根据权重计算的routeId     
public boolean test(ServerWebExchange exchange) {
Map
weights = exchange.getAttributeOrDefault(WEIGHT_ATTR,
Collections.emptyMap());
//该值是在RoutePredicateHandlerMapping的lookupRoute方法中,遍历所有配置的路由
//每次遍历,将值放入到GATEWAY_PREDICATE_ROUTE_ATTR属性中,有所有的RoutePredicateFactory来执行
String routeId = exchange.getAttribute(GATEWAY_PREDICATE_ROUTE_ATTR);
String group = config.getGroup();
if (weights.containsKey(group)) {
String chosenRoute = weights.get(group);
return routeId.equals(chosenRoute);
}
return
false;
}

12、ReadBodyPredicateFactory

  • 缓存请求体,这样可以在后续多次读取请求体

  • 该Predicate在官方文档上没有说明。

  • 参考网上资料:

    https://www.cnblogs.com/hyf-huangyongfei/p/12849406.html

最后:

  • 不同的Predicate可以组合配置,但是需要同时满足才匹配成功。

  • 如果一个请求能满足多个配置,则请求会被转发到第一个匹配的URI。

 

你可能感兴趣的文章
java数据结构-数据结构的概述
查看>>
java -Math常用方法
查看>>
Android进阶系列-手写数据库框架
查看>>
算法入门-循环结构程序设计
查看>>
算法入门-数组和字符串
查看>>
Android进阶系列-手写高并发网络访问框架
查看>>
Java基础之线程安全基本数据类型
查看>>
Android进阶系列-手写高并发图片加载框架
查看>>
Android基础系列-大纲汇总
查看>>
Android测试系列(一)-Monkey
查看>>
Android动画系列(一) - 基础动画ViewAnimation
查看>>
Android进阶系列 - 小视频录制取消及播放
查看>>
Android测试系列(二)- JMeter
查看>>
Android NDK系列(一)-AS使用javah生成so文件
查看>>
Linux常用SVN 操作
查看>>
Qos之丢包重传NACK
查看>>
怎么让UDP可靠?
查看>>
只问初心,无问西东
查看>>
nginx安装参考
查看>>
Redis安装参考
查看>>