断言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自定义处理。