二十三、SpringCloud Gateway 的初体验

Gateway的路由谓词工厂

相关概念(术语)

  • 路由 (Route): 路由是网关的基础构建模块,它是有ID,目标URI,谓词集合和过滤器集合定义。如果聚合谓词为true,则匹配路由。
  • 谓词 (Predicate): 这是Java8的函数谓词,输入类型是Spring Framework ServerWebExchange, 这使得开发者可以匹配HTTP请求中的所有内容,例如 header 和 参数。
  • 过滤器 (Filter): 这些是使用特定工厂构建的SpringFramework GatewayFilter 实例。在此,可以在发送下游请求之前或之后修改请求和响应。

路由谓词工厂

SpringCloudGateway 将路由作为 Spring WebFlux HandlerMapping 基础架构的一部分进行匹配。SpringCloudGateway 包括许多内置的Route Predicate factory。所有这些谓词都与HTTP请求的不同属性匹配。多个Route Predicate工厂可以合并,也可以通过逻辑合并 and

1. After 路由谓词

After Route Predicate Factory 采用一个参数,即日期时间。该谓词匹配在当前日期时间之后发生的请求。

2. Before 路由谓词

Before Route Predicate Factory 采用一个参数,即日期时间。该谓词匹配在当前日期时间之前发生的请求。

3. Between 路由谓词

Between Route Predicate Factory 采用两个参数,都是日期时间类型的,该谓词匹配在两个日期之间发生的请求。

下面是统一的配置:

1
2
3
4
5
6
7
8
9
10
spring:
cloud:
gateway:
routes:
- id: gateway-service
uri: https://www.163.com
predicates:
# - Before= 2019-10-25T00:00:00+08:00[Asia/Shanghai]
# - After= 2019-10-25T00:00:00+08:00[Asia/Shanghai]
- Between= 2019-10-25T00:00:00+08:00[Asia/Shanghai],2019-10-25T18:05:00+08:00[Asia/Shanghai]

说明: 如果是时间之前的,使用Before关键字。如果是时间之后的,使用After关键字,如果要指明在两个日期之间,则使用Between 关键字,两个日期使用逗号隔开。[Asia/Shanghai] 表示使用的是以上海为基准的时间。

Cookie路由谓词工厂采用的是两个参数,一个是Cookie的名字,另外一个是合法的正则表达式。这个谓词用于匹配具有给定名称的Cookie,并且值匹配一个合法的正则表达式。

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: gateway-service
uri: http://www.163.com
predicates:
- Cookie= username,kee.e

说明:这里是采用验证cookie的方式,当我们请求中携带了对应的正确的cookie信息,就可以访问成功到http://www.163.com , 否则就会报 Not Found .

下面是使用Postman测试的结果:

错误的cookie信息:

正确的cookie信息:

如果使用gateway时, 需要取出 cookie信息,可以使用下面的方法:

1
2
3
4
@GetMapping("/resp")
public String test(@CookieValue("username") String username) {
return "SUCCESS";
}

5. Header 路由谓词

Header 路由谓词工厂接受两个参数:header的name和其对应的合法的正则表达式值。只有在Header部分存在name并且其value也匹配的情况下,才能通过。

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: gateway-service
uri: http://www.163.com
predicates:
- Header=password,123456

测试结果如下:

如果使用Gateway,需要从Header中取出信息,可以使用下面的方法:

1
2
3
4
@GetMapping(value = "/testHeader")
public String testHeader(@RequestHeader(value = "username") String username){
return "username:"+username;
}

6. Host 路由谓词

Host(主机)路由谓词工厂,采用一个参数:主机名模式列表。该模式带有. 作为分隔符的Ant样式的模式,谓词与Host 匹配模式的Header部分。

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: gateway-service
uri: http://localhost:8888
predicates:
- Host= localhost:9999

这里为了更好的测试,我们单创建并启动一个服务,端口为8888的,大概代码如下:

1
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
/**
* @author hao.ouYang
* @create 2019-10-25 18:08
*/
@RestController
public class UserController {
@Autowired
private UserService userService;

@RequestMapping("/user")
public User findUser(){
return userService.findUser();
}
}

@Service
public class UserService {
public User findUser(){
User user = new User(1,"admin","admin");
return user;
}
}

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {
private Integer id;
private String username;
private String password;
}

测试结果如下:

上面其实是将http://localhost:9999/user 映射到了 http://localhost:8888/user 上了。

注意:当然除了我们除了上面的写法,还有一些多样化的定制。同时也支持URI模板变量

1
2
3
- Host= www.ouyang.**, **.ouyang.**,**.ouyang.org
# 上面的例子可以匹配 www.ouyang.club, www.ouyang.com, www.ouyangorg等。
- Host= {sub}.ouyang.club

7. Method 路由谓词

方法路由谓词工厂采用一个参数:用来匹配HTTP请求的类型。(GET,POST等).

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: gateway-service
uri: http://localhost:8888
predicates:
- Method= POST

以上需要匹配为POST 类型的请求才可以转发到 http://localhost:8888 上面,测试如下:

更改为POST 类型的请求:

8. Path 路由谓词

PathRoutePredicateFactory 需要PathMatcher 模式路径列表和一个可选的标志位参数matchOptionalTrailingSeparator. 这是最常用的一个路由谓词。

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: gateway-service
uri: http://localhost:8888
predicates:
- Path= /path

这种使用路径来映射的方式。以上可以将http://localhost:9999/path 映射到http://localhost:8888/path 路径上,前一节也见过这个谓词,所以说这个谓词是最常用。还可以通过下面这个方式在请求路径上携带参数:(如果通过这种形式配置,在匹配命中进行路由的同时,会提取路径中对应的内容并且将键值对放在ServerWebExchange.getAttributes()集合中,Key为ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE, 这些提取出来的属性可以供GatewayFilter Factories 使用)。

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: gateway-service
uri: http://localhost:8888
predicates:
- Path= /testPath/{stri}

另一个服务的接收:

1
2
3
4
5
6
7
8
9
@GetMapping(value = "/path")
public String path(){
return "path";
}

@GetMapping(value = "/testPath/{str}")
public String testPath(@PathVariable("str") String str){
return "return:"+str;
}

9. Query 路由谓词

请求查询参数路由工厂QueryRoutePredicateFactory 需要一个必须的请求查询参数(Param的name)以及一个可选的正则表达式(regexp).

1
2
3
4
5
6
7
8
9
10
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: gateway-service
uri: http://localhost:8888
predicates:
- Query= username

通过上述配置,我们的请求中只需要包含username参数即可匹配路由。

1
curl localhost:9999/testQuery?username=1

上面的测试可以通过,但是如果把username 删除或是改成别的,就无法匹配了。

还可以将Query参数以键值对的形式来配置,这样的请求过来的时候,不仅需要匹配名字,同时参数值需要与正则表达式匹配才能走路径。

1
2
3
4
5
6
7
8
9
10
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: gateway-service
uri: http://localhost:8888
predicates:
- Query= username,ad.

以上匹配需要请求满足既有username 参数,并且值需要以ad 开头的长度为3的字符串才能匹配和路由。

测试如下:

1
2
3
4
5
curl http://localhost:9999/testQuery?username=adv
{"param":"adv"}

curl http://localhost:9999/testQuery?username=addv
{"timestamp":"2019-10-27T09:15:45.580+0000","path":"/testQuery","status":404,"error":"Not Found","message":null}

8888端口服务的controller方法为:

1
2
3
4
5
6
@GetMapping(value = "/testQuery")
public Map<String,String> testQuery(@RequestParam("username") String username){
Map<String,String> map = new HashMap<>();
map.put("param",username);
return map;
}

10. RemoteAddr 路由谓词

RemoteAddrRoutePredicateFactory匹配规则采用CIDR符号(IPv4或IPv6)字符串的列表(最小值为1),例如192.168.0.1/16(其中192.168.0.1是远程IP地址并且16是子网掩码)。

1
2
3
4
5
6
7
8
9
10
11
12
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: gateway-service
uri: http://localhost:8888
predicates:
- RemoteAddr= 127.0.0.1
server:
port: 9999

8888端口服务的处理方法:

1
2
3
4
@GetMapping(value = "/remote")
public String testRemote(){
return "Remote";
}

测试结果:

1
2
3
curl http://127.0.0.1:9999/remote
# 响应结果
Remote

11. Weight 路由谓词

权重路由谓词工厂采用两个参数,分别为组(group)和权重(weight). 权重按组进行计算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: gateway-weight-high
uri: http://localhost:8888
predicates:
- Weight= Group1, 8
- id: gateway-weight-low
uri: http://localhost:7777
predicates:
- Weight= Group1, 2
server:
port: 9999

接受7777和8888请求的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//============port:7777 ============
@GetMapping(value = "/weight")
public Map weight() {
Map<String, Object> map = new HashMap<>();
map.put("port", "7777");
return map;
}

//============port:8888 ============
@GetMapping(value = "/weight")
public Map weight() {
Map<String, Object> map = new HashMap<>();
map.put("port", "8888");
System.out.println(map);
return map;
}

测试结果如下:

参考了: https://www.cnblogs.com/throwable/p/10807704.html

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×