hello云胜

技术与生活

0%

SpringCloud Gateway内置过滤器

基于2.2.4Release。Spring Cloud Gateway

SpringCloud Gateway的filter从作用范围上分有两种

  1. 针对于单个路由的gateway filter,它在配置文件中的写法同predict类似
  2. 针对于所有路由的global filer。不需要在配置文件中配置,即对所有路由生效。

GatewayFilter

Spring Cloud Gateway

为了方便起见,springcloud已经内置了很多常用的过滤器,我们应该知晓这些官方过滤器的存在,避免重复造轮子。具体每个过滤器的用法细节用到时再细究。

过滤器名 作用
AddRequestHeader 为原始请求添加Header
AddRequestParameter 为原始请求添加请求参数
AddResponseHeader 为原始响应添加Header
DedupeResponseHeader 剔除响应头中重复的值
Hystrix 为路由引入Hystrix的断路器保护
FallbackHeaders 为fallbackUri的请求头中添加具体的异常信息
PrefixPath 为原始请求路径添加前缀
PreserveHostHeader 为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host
RequestRateLimiter 用于对请求限流,限流算法为令牌桶
RedirectTo 将原始请求重定向到指定的URL
RemoveHopByHopHeadersFilter 为原始请求删除IETF组织规定的一系列Header 默认就会启用,可以通过配置指定仅删除哪些Header
RemoveRequestHeader 为原始请求删除某个Header
RemoveResponseHeader 为原始响应删除某个Header
RewritePath 重写原始的请求路径
RewriteResponseHeader 重写原始响应中的某个Header
SaveSession 在转发请求之前,强制执行WebSession::save操作
SecureHeaders 为原始响应添加一系列起安全作用的响应头
SetPath 修改原始的请求路径
SetResponseHeader 修改原始响应中某个Header的值
SetStatus 修改原始响应的状态码
StripPrefix 用于截断原始请求的路径
Retry 针对不同的响应进行重试
RequestSize 设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返回 413 Payload Too Large 请求包大小,单位为字节,默认值为5M
ModifyRequestBody 在转发请求之前修改原始请求体内容
ModifyResponseBody 修改原始响应体的内容
Default 为所有路由添加过滤器 也就是说通过Default Filter所配置的过滤器工厂会作用到所有的路由上
MapRequestHeader 添加一个新的header,其值为一个已经存在的header的值。 fromHeader和toHeader两个参数

注意:每个过滤器工厂都对应一个实现类,并且这些类的名称必须以GatewayFilterFactory结尾,这是Spring Cloud Gateway的一个约定,例如AddRequestHeader对应的实现类为AddRequestHeaderGatewayFilterFactory。对源码感兴趣的小伙伴就可以按照这个规律拼接出具体的类名,以此查找这些内置过滤器工厂的实现代码

使用方法举例

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: <https://example.org>
filters:
- AddRequestHeader=My-Header, Hello

这样即可为原始请求添加名为 My-Header ,值为 Hello的请求头

配置默认过滤器,即Default的用法

1
2
3
4
5
spring:
cloud:
gateway:
default-filters:
- PrefixPath=/base

GlobalFilter

Spring Cloud Gateway

不需要进行配置,默认已经生效了。

但我们必须了解其功能,以备不时之需。

ForwardRoutingFilter

这个过滤器会根据formord模式规则将情况转发给DispatcherHandler,然后转发给gateway网关自己的服务中

处理uri的scheme是forward的请求

forward模式就是forward:///localendpoint这种形式

举个例子来理解

1
2
3
4
5
6
7
- id: forward_routing_filter
uri: forward:///app
order: 10000
predicates:
- Path=/forwardFilterTest
filters:
- PrefixPath=/gateway

还要在gateway项目中创建个服务

1
2
3
4
5
6
7
8
@RestController
@RequestMapping("gateway")
public class FowardRoutingFilterController {
@RequestMapping("app")
public String globalFilters() {
return "Forward跳转成功";
}
}

我们输入http://localhost:8080/forwardFilterTest进行测试。这个请求最终会进到/gateway/app这个服务中

因为在spring-cloud-gateway服务收到请求之后,会执行以下步骤

  1. 根据请求路径/forwardFilterTest匹配到路由forward_routing_filter,并将请求跳转为:http://localhost:8080/app

  2. filters里面的PrefixPath配置请求改写为:http://localhost:8080/gateway/app

  3. ForwardRoutingFilter过滤器中判断路由中有foroward://前缀,将请求转发给DispatcherHandler

  4. DispatcherHandler匹配并转到spring-cloud-gateway服务中的contoller匹配的路径

总之,这种转发的重点在于转发给了gateway本身项目的服务。

使用场景不多。

LoadBalancerClientFilter

负载均衡过滤器。这个是非常常用到的过滤器。

因为网关后面的服务可以启动多个实例,使用负载均衡过滤器可以自动根据负载均衡规则路由到某台服务实例上面。

处理uri的scheme是lb的请求

使用这个过滤器的规则就是匹配到负载均衡的模式,即lb://xxx

举个例子

1
2
3
4
5
6
7
8
spring:
cloud:
gateway:
routes:
- id: myRoute
uri: lb://service
predicates:
- Path=/service/**

uri写为lb://service,即可在转发时使用ReactiveLoadBalancerClientFilter 。

service的名字即为spring.application.name的值

注意:默认情况下,如果LoadBalancer根据配置的实例名找不到有效的服务实例,返回状态码503,如果你想配置返回404,可以这样设置:

1
spring.cloud.gateway.loadbalancer.use404=true

NettyRoutingFilter

这是一个优先级最低的过滤器。

1
2
3
4
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}

处理uri的sechme说http或者https的请求。

因为他的优先级最低,也就是pre流程的最后一个filter。它将使用Netty的HttpClient创建向下执行的请求代理。

前面说过,请求分为pre过程和post过程。那么springcloud-gateway是怎么区分一个请求目前是处在那个过程的呢?

答案就在NettyRoutingFilter里。

NettyRoutingFilter是请求进来进行处理的最后一个filter,所以在NettyRoutingFilter的代码中,

1
exchange.getAttributes().put(CLIENT_RESPONSE_CONN_ATTR, connection);

会加上CLIENT_RESPONSE_CONN_ATTR一个属性。

之后的请求都带上这个属性,springcloud-gateway判断有这个属性的就是post类型的filter。比如下面的NettyWriteResponseFilter

NettyWriteResponseFilter

它的优先级是最高的。所以他是在前面的filter。

并且他是post类型的过滤器。也就相当于是进行响应处理的最后一个filter

经过他之后,就将响应的数据发送给网关的客户端了。

1
2
3
4
5
public static final int WRITE_RESPONSE_FILTER_ORDER = -1;
@Override
public int getOrder() {
return WRITE_RESPONSE_FILTER_ORDER;
}

为什么他是post类型的过滤器?

上面说到springcloud-gateway判断有CLIENT_RESPONSE_CONN_ATTR属性的就是post类型的filter

看源码

1
2
3
4
5
Connection connection = exchange.getAttribute(CLIENT_RESPONSE_CONN_ATTR);

if (connection == null) {
return Mono.empty();
}

NettyWriteResponseFilter会判断CLIENT_RESPONSE_CONN_ATTR属性,如果没有,直接返回不做任何处理。

所以,因为NettyWriteResponseFilter的order是-1。

一般会排在最前面,所以请求首先会进来,但是因为当前没有CLIENT_RESPONSE_CONN_ATTR属性,所以就算经过NettyWriteResponseFilter也不会做任何处理。

等到过了NettyRoutingFilter,加上了CLIENT_RESPONSE_CONN_ATTR属性,再回来的时候就会得到NettyWriteResponseFilter的处理了。这也就是post过程了。

WebsocketRoutingFilter

很明显处理websocket的过滤器。

处理uri的scheme是ws或wss的请求。

1
2
3
4
- id: websocket_route
uri: ws://localhost:3001
predicates:
- Path=/websocket/**

也可以对ws进行负载均衡,比如:lb:ws://serviceid

RouteToRequestUrlFilter

处理的是请求的属性中有gatewayRoute这个属性的请求。

它的作用是根据route的uri配置,重新修改请求的URL地址

比如浏览器请求网关的URL是:http://localhost:8080/app-a/app/balance,

路由的URI配置是:uri: lb://app-a

那么修改之后的路由的URI是:lb://app-a/app/balance