献给Gateway小白的一篇好文:断言Predicate

Posted 云水之路

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了献给Gateway小白的一篇好文:断言Predicate相关的知识,希望对你有一定的参考价值。

断言Predicate

Spring Cloud Gateway(简称Gateway)支持断言Predicate功能,该断言功能是基于Spring WebFlux的HandlerMapping实现的。Gateway包含了很多路由断言工厂,并且这些工厂对应着HTTP请求的很多属性进行了处理,当客户端HTTP请求时,HandlerMapping会获取请求参数,并与Gateway中配置的Predicates进行比对,若满足规则就按规则约定进行路由放行,否则拒绝访问或报404错误。

|如何使用断言

断言如何工作

| 一些注意事项

一、如何使用断言

在Gateway中,使用断言Predicate比较简单,其提供了简单的配置就可实现断言功能,我们需在如下位置配置:

spring.cloud.gateway.routes.predicates,常见断言用法如下:

1、After

After只接受一个参数,即DateTime格式时间,客户端访问Gateway接口的时间在After指定之后的时间是允许访问的,否则,当前访问被拦截:

- Path=/api/test/**
- After=2022-08-01T15:59:59Z[Asia/Shanghai]

 

当请求接口到来时,该断言工厂AfterRoutePredicateFactory通过apply判别请求接口时间是否大于在所配置时间,若是则放行,否则觉得访问,返回404错误。

2、Before

Before只接受一个参数,即DateTime格式时间,客户端访问Gateway接口的时间在Before指定之前的时间是允许访问的,否则,当前访问被拦截:

- Path=/api/test/**
- Before=2022-08-01T15:59:59Z[Asia/Shanghai]

当请求接口到来时,该断言工厂BeforeRoutePredicateFactory通过apply判别请求接口时间是否小于在所配置时间,若是则放行,否则觉得访问,返回404错误。

3、Between

Between接受两个参数,格式依然为DateTime格式时间,客户端访问Gateway接口的时间在Between指定的时间区间之内是允许访问的,否则,当前访问被拦截:

- Path=/api/test/**
- Between=2022-08-01T15:59:59Z[Asia/Shanghai], 2022-08-02T15:59:59Z[Asia/Shanghai]

4、Cookie

Cookie用来指定当前客户端请求头的cookie设置,只有当客户端请求头传递了cookie并且值与gateway设置的相等则放行,否则请求被拦截:

- Path=/api/test/**
- Cookie=test-001,hello-gateway

如何设置当前请求的Cookie昵?我们可使用postman来指定请求的cookie,具体设置如下:

5、Header

Header用来设定请求头匹配属性的,当前请求头属性与gateway所指定属性规则相同时,gateway断言放行该请求,否则拒绝访问,具体如下配置:

- Path=/api/test/**
- Header=Cus-Test-Request-Id, \\d+

如上配置\\d+为正则表达式,代表属性Cus-Test-Request-Id的值可匹配若干数字串,使用postman配置如下:

6、Host

Host用来匹配当前请求的host规则,该参数一般为自动计算,不需要手动设置,只有当前请求头中的host满足gateway所设定支持的host规则时,断言才会放行请求,否则截断请求:

- Path=/api/test/**
- Host=localhost,**.xxx.com,127.0.0.1:10000

如上,一般需要指定IP或域名+端口号,若端口为80则可不指定,其中**代表任意名字的域名地址。上面配置的127.0.0.1:10000满足当前网关的IP+端口号,故请求放行。

7、Method

Method用来指定gateway断言支持的请求方式,如:GET、POST或是PUT等,目前大多用的是GET和POST方式,只有请求方式在gateway所设定支持请求方式范围内,则放行请求:

- Path=/api/test/**
- Method=GET,POST

如上代表支持GET和POST两种请求方式,其它方式则被拒绝。在postman中体现如下,改为GET请求即可放行:

8、Path

正如上面所设定的- Path=/api/test/**所示,代表gateway所支持的路由接口地址,其中**代表任何级别的接口名,如:/api/test/hello-world或/api/test/hello/world等,Path还可指定单个级别接口,配置规则:- Path=/api/test/segment,如:/api/test/hello,而不支持/api/test/hello/world格式:

predicates:
  - Path=/api/test/segment,/api2/test2/segment

9、Query

Query用来指定请求的查询参数,当前请求必须传递查询参数,并且传递的查询参数必须与gateway所指定的完全相同,否则断言拒绝当前请求,具体如下:

- Path=/api/test/**
- Query=hello,world!

在postman中可如下填写查询参数:

10、RemoteAddr

RemoteAddr用来设定断言所支持的IP网段,格式:IP地址/子网掩码,如果当前请求的IP地址在RemoteAddr所指定的IP段内,那么,gateway断言放行该请求:

- Path=/api/test/**
- RemoteAddr=192.168.43.1/24

此时,我们访问的接口地址IP必须属于192.168.43.x网段,否则访问失败,正确的访问地址:

http://192.168.43.146:10000/api/test/hello-world

11、Weight

Weight是用来指定当前请求被路由的权重的,其接收两个参数:分组和比重,参与权重路由的地址必须在同一个分组,否则weight无效果,而权重为整型数字:

id: client-service
  
非负载均衡路由
  uri: http://127.0.0.1:10002
  
predicates:
    
- Path=/api/test/**
    - Weight=group1,8 
  
filters:
    
- StripPrefix=1

### client service ###
id: client-service2
  
负载均衡的路由
  uri: http://127.0.0.1:10003
  
predicates:
    
- Path=/api/test/**
    - Weight=group1,2
  
filters:
    
- StripPrefix=1

如上所示,为了便于区分权重路由,这里uri不使用负载调度,而是不同的weight分别调取对应的服务实例接口,如上即代表访问client-service服务实例端口为10002的权重为80%,10003端口实例访问概率为20%,所以,实际测试时,可连续点击请求该接口,正常会看到如下结果出现概率最大:

hello-world ,I'am coming form the current service of port:10002

二、断言如何工作

1、断言工厂

网关Gateway提供了很多针对HTTP请求的断言工厂,如:AfterRoutePredicateFactory、PathRoutePredicateFactory及HostRoutePredicateFactory等,这些工厂均继承至抽象类AbstractRoutePredicateFactory,而这个抽象类则实现了RoutePredicateFactory接口,这是一种抽象化思想,尽量做到解耦合和更好拓展性,而具体的断言判别逻辑则是在各自工厂的apply方法中,通过GatewayPredicate路由断言接口的test方法判别实现。

2、流程逻辑

我们知道,Gateway的工作流程是这样的:客户端请求->A:Gateway Handler Mapping接收请求参数并做相关断言处理->B:Web Handler Mapping->GlobalFilter->各种自定义Filter逻辑->目标服务接口调度,其中A->B的流转是通过代理GatewayFilter来实现的,进而流转到各种FilterChain链。

Spring WebFlux的HandlerMapping负责获取当前请求的各种参数,并下发到各种子处理HandlerMapping中,这里与断言直接相关的是RoutePredicateHandlerMapping,该类继承至抽象类AbstractHandlerMapping,而这个抽象类又实现了HandlerMapping接口。

当前请求到来会调取RoutePredicateHandlerMapping类的getHandlerInternal方法,进而通过RouteDefinitionRouteLocator(该类实现了接口RouteLocator)实现类中的getRoutes获取已配置的predicates,同时在该方法中转换封装好需要的Route实体返回,而具体的断言拦截功能通过Route中的AsyncPredicate的apply方法中再调取Predicate的test方法实现断言功能的,这就是从源码层面分析得到的断言工作流程。

三、一些注意事项

1、断言时间格式

断言配置选项中,涉及的时间格式是ZonedDateTime类型,建议大陆时区设置为[Asia/Shanghai]。

2、断言时间范围

如果设置的断言时间不符合日期规范,编译时会直接报错,如:

- After=2022-06-31T15:59:59Z[Asia/Shanghai]

如上,假如我们设置时间为2022-06-31 23:59:59之后放行请求,这是有问题的,因为06月份不包含31号,所以编译会出错。

3、不要乱用

建议断言中配置一些需要优先验证,且常规固定的HTTP参数校验,很多其它拓展功能建议使用Filter自定义处理。 

以上是关于献给Gateway小白的一篇好文:断言Predicate的主要内容,如果未能解决你的问题,请参考以下文章

献给Gateway小白的一篇好文:断言Predicate

献给Gateway小白的一篇好文:Spring Cloud Gateway网关

献给Gateway小白的一篇好文:Spring Cloud Gateway网关

献给Gateway小白的一篇好文:Spring Cloud Gateway网关

献给Nacos小白的一篇好文:注册中心

献给Nacos小白的一篇好文:注册中心