献给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自定义处理。 

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Gateway自定义断言可以通过实现`GatewayFilterFactory`接口来创建。在实现`GatewayFilterFactory`接口的`apply`方法中,可以使用`ExchangeFilterChain`对象来实现自定义的断言逻辑。以下是一个简单的示例代码: ```java @Component public class CustomPredicateGatewayFilterFactory implements GatewayFilterFactory<CustomPredicateGatewayFilterFactory.Config> { public static final String MY_CUSTOM_HEADER = "my-custom-header"; @Override public GatewayFilter apply(Config config) { return (exchange, chain) -> { HttpHeaders headers = exchange.getRequest().getHeaders(); if (headers.containsKey(MY_CUSTOM_HEADER)) { String value = headers.getFirst(MY_CUSTOM_HEADER); if (config.getValue().equals(value)) { return chain.filter(exchange); } } return exchange.getResponse().setComplete(); }; } @Override public Config newConfig() { return new Config(); } @Override public Class<Config> getConfigClass() { return Config.class; } public static class Config { private String value; public String getValue() { return value; } public void setValue(String value) { this.value = value; } } } ``` 在上面的代码中,我们定义了一个名为`CustomPredicateGatewayFilterFactory`的类,该类实现了`GatewayFilterFactory`接口。我们使用`Config`类来保存配置信息。在`apply`方法中,我们检查请求头中是否包含`my-custom-header`,如果存在,则检查其值是否与`Config`中的值相同。如果相同,则继续执行请求链;否则,返回响应并结束请求。通过这种方式,我们可以实现自定义的断言逻辑。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云水之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值