Spring Cloud Gateway 内置的全局过滤器(Global Filters)
Spring Cloud Gateway 内置的全局过滤器(Global Filters)
本文基于 Spring Cloud Greenwich SR2
原文链接:https://www.jianshu.com/p/3ab97acf1e69
这里将 Spring Cloud Gateway 内置的所有全局过滤器简单整理成了一张表格,用作速览:
| 全局过滤器 | 作用 |
|---|---|
| Forward Routing Filter | 用于本地 forward,也就是将请求在 Gateway 服务内进行转发,而不是转发到下游服务 |
| LoadBalancerClient Filter | 整合 Ribbon 实现负载均衡 |
| Netty Routing Filter | 使用 Netty 的 HttpClient 转发 http、https 请求 |
| Netty Write Response Filter | 将代理响应写回网关的客户端侧 |
| RouteToRequestUrl Filter | 将从 request 里获取的原始 url 转换成 Gateway 进行请求转发时所使用的 url |
| Websocket Routing Filter | 使用 Spring Web Socket 将转发 Websocket 请求 |
| Gateway Metrics Filter | 整合监控相关,提供监控指标 |
1、Combined Global Filter and GatewayFilter Ordering
当 Gateway 接收到请求时,Filtering Web Handler 处理器会将所有的 GlobalFilter 实例以及所有路由上所配置的 GatewayFilter 实例添加到一条过滤器链中。该过滤器链里的所有过滤器都会按照 org.springframework.core.Ordered 注解所指定的数字大小进行排序。
Spring Cloud Gateway 区分了过滤器逻辑执行的 ”pre” 和 ”post” 阶段,所以优先级高的过滤器将会在 “pre” 阶段最先执行,优先级最低的过滤器则在 “post” 阶段最后执行。
**Tips:**数字越小越靠前执行,记得这一点就 OK 了
代码示例:
@Bean
@Order(-1)
public GlobalFilter a() {
return (exchange, chain) -> {
log.info("first pre filter");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("third post filter");
}));
};
}
@Bean
@Order(0)
public GlobalFilter b() {
return (exchange, chain) -> {
log.info("second pre filter");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("second post filter");
}));
};
}
@Bean
@Order(1)
public GlobalFilter c() {
return (exchange, chain) -> {
log.info("third pre filter");
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
log.info("first post filter");
}));
};
}
执行结果:
first pre filter
second pre filter
third pre filter
first post filter
second post filter
third post filter
2、Forward Routing Filter
简单来说这个 Filter 是用来做本地 forward 的,将官方文档的描述翻译后大致如下:
当请求进来时,ForwardRoutingFilter 会查看一个 URL,该 URL 为 exchange 属性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的值,如果该 url 的 scheme 是 forward(例如:forward://localendpoint),那么该 Filter 会使用 Spirng 的 DispatcherHandler 来处理这个请求。该请求的 URL 路径部分,会被 forward URL 中的路径覆盖掉。而未修改过的原始 URL,会被追加到 ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR 属性中
注:所谓 url scheme 简单来说就是 url 中的协议部分,例如 http、https、ws 等。自定义的 scheme 通常用于标识该 url 的行为,例如 app 开发中通常使用 url scheme 来跳转页面
Tips:
- 这段文档实际上是描述了该 Filter 的实现原理,对使用者来说意义不大,对实现原理感兴趣的话可以直接查看源码,源码比文档描述好理解:
org.springframework.cloud.gateway.filter.ForwardRoutingFilter
3、LoadBalancerClient Filter
这个 Filter 是用来整合 Ribbon 的,其核心就是解析 scheme 为lb的 url,以此获取微服务的名称,然后再通过 Ribbon 获取实际的调用地址。将官方文档的描述翻译后大致如下:
当请求进来时,LoadBalancerClientFilter 会查看一个 URL,该 URL 为 exchange 的属性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的值,如果该 url 的 scheme 是 lb,(例如:lb://myservice ),那么该 Filter 会使用 Spring Cloud 的 LoadBalancerClient 来将 myservice 解析成实际的 host 和 port ,并替换掉原本 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 属性的值。而原始 url 会追加到 ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR 属性中。该过滤器还会查看 ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR 属性,如果发现该属性的值是 lb ,也会执行相同逻辑。
配置示例:
spring:
cloud:
gateway:
routes:
- id: myRoute
uri: lb://service
predicates:
- Path=/service/**
默认情况下,如果无法通过 LoadBalancer 找到指定服务的实例,那么会返回 503(如上配置示例, 若 LoadBalancer 找不到名为 service 的实例时,就会返回 503);可使用配置: spring.cloud.gateway.loadbalancer.use404=true ,让其返回 404。
LoadBalancer 返回的 ServiceInstance 的 isSecure 的值,会覆盖请求的 scheme。举个例子,如果请求打到 Gateway 上使用的是 HTTPS ,但 ServiceInstance 的 isSecure 是false,那么下游微服务接收到的则是 HTTP 请求,反之亦然。另外,如果该路由指定了 GATEWAY_SCHEME_PREFIX_ATTR 属性,那么前缀将会被剥离,并且路由 URL 中的 scheme 会覆盖 ServiceInstance 的配置
Tips:
- 这段文档实际上是描述了该 Filter 的实现原理,对使用者来说意义不大,对实现原理感兴趣的话可以直接查看源码,源码比文档描述好理解:
org.springframework.cloud.gateway.filter.LoadBalancerClientFilter
4、Netty Routing Filter
当请求进来时,NettyRoutingFilter 会查看一个 URL,该 URL 是 exchange 的属性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的值,如果该 url 的 scheme 是 http 或 https ,那么该 Filter 会使用 Netty 的 HttpClient 向下游的服务发送代理请求。获得的响应将放在 exchange 的 ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR 属性中,以便在后面的 Filter 里使用。(有一个实验性的过滤器: WebClientHttpRoutingFilter 可实现相同功能,但无需 Netty)
5、Netty Write Response Filter
NettyWriteResponseFilter 用于将代理响应写回网关的客户端侧,所以该过滤器会在所有其他过滤器执行完成后才执行,并且执行的条件是 exchange 中 ServerWebExchangeUtils.CLIENT_RESPONSE_CONN_ATTR 属性的值不为空,该值为 Netty 的 Connection 实例。(有一个实验性的过滤器: WebClientWriteResponseFilter 可实现相同功能,但无需 Netty)
6、RouteToRequestUrl Filter
这个过滤器用于将从 request 里获取的原始 url 转换成 Gateway 进行请求转发时所使用的 url。当请求进来时,RouteToRequestUrlFilter 会从 exchange 中获取 ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR 属性的值,该值是一个 Route 对象。若该对象不为空的话,RouteToRequestUrlFilter 会基于请求 URL 及 Route 对象里的 URL 来创建一个新的 URL。新 URL 会被放到 exchange 的 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 属性中。
可能有点抽象,我们 debug 一下,看看这三个 url 的值就明白了,如下图:

image.png
如果 URL 具有 scheme 前缀,例如 lb:ws://serviceid ,该 lb scheme 将从 URL 中剥离,并放到 ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR 中,方便后面的过滤器使用。
7、Websocket Routing Filter
该过滤器的作用与 NettyRoutingFilter 类似。当请求进来时,WebsocketRoutingFilter 会查看一个 URL,该 URL 是 exchange 中 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 属性的值,如果该 url 的 scheme 是 ws 或者 wss,那么该 Filter 会使用 Spring Web Socket 将 Websocket 请求转发到下游。
另外,如果 Websocket 请求需要负载均衡的话,可为 URL 添加 lb 前缀以实现负载均衡,例如 lb:ws://serviceid 。
如果你使用 SockJS 作为普通 http 的后备,则应配置正常的 HTTP 路由以及 Websocket 路由。
配置示例:
spring:
cloud:
gateway:
routes:
# SockJS route
- id: websocket_sockjs_route
uri: http://localhost:3001
predicates:
- Path=/websocket/info/**
# Normwal Websocket route
- id: websocket_route
uri: ws://localhost:3001
predicates:
- Path=/websocket/**
8、Gateway Metrics Filter
想要启用 Gateway Metrics Filter,需在项目中添加 spring-boot-starter-actuator 依赖,然后在配置文件中配置 spring.cloud.gateway.metrics.enabled 的值为true。该过滤器会添加名为 gateway.requests 的时序度量(timer metric),其中包含以下标记:
routeId:路由 IDrouteUri:API 将路由到的 URIoutcome:由 HttpStatus.Series 分类status:返回给客户端的 Http StatushttpStatusCode:返回给客户端的请求的 Http StatushttpMethod:请求所使用的 Http 方法
这些指标暴露在 /actuator/metrics/gateway.requests 端点中,并且可以轻松与 Prometheus 整合,从而创建一个 Grafana dashboard。
注:Prometheus 是一款监控工具,Grafana 是一款监控可视化工具;Spring Boot Actuator 可与这两款工具进行整合。
9、Marking An Exchange As Routed
当一个请求走完整条过滤器链后,负责转发请求到下游的那个过滤器会在 exchange 中添加一个 gatewayAlreadyRouted 属性,从而将 exchange 标记为 routed(已路由)。一旦请求被标记为 routed ,其他路由过滤器将不会再次路由该请求,而是直接跳过。
了解了以上所有内置的全局过滤器后,我们知道不同协议的请求会由不同的过滤器转发到下游。所以负责添加这个gatewayAlreadyRouted 属性的过滤器就是最终负责转发请求的过滤器:
- http、https 请求会由
NettyRoutingFilter或WebClientHttpRoutingFilter添加这个属性 - forward 请求会由
ForwardRoutingFilter添加这个属性 - websocket 请求会由
WebsocketRoutingFilter添加这个属性
这些过滤器调用了以下方法将 exchange 标记为 routed ,或检查 exchange 是否是 routed:
ServerWebExchangeUtils.isAlreadyRouted:检查 exchange 是否为 routed 状态ServerWebExchangeUtils.setAlreadyRouted:将 exchange 设置为 routed 状态
简单来说,就是 Gateway 通过 gatewayAlreadyRouted 属性表示这个请求已经转发过了,而无需其他过滤器重复路由,从而防止重复的路由转发。
这些全局过滤器都有对应的配置类,感兴趣的话可以查看相关源码:
org.springframework.cloud.gateway.config.GatewayAutoConfigurationorg.springframework.cloud.gateway.config.GatewayMetricsAutoConfigurationorg.springframework.cloud.gateway.config.GatewayLoadBalancerClientAutoConfiguration
官方文档:
