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.GatewayAutoConfiguration
org.springframework.cloud.gateway.config.GatewayMetricsAutoConfiguration
org.springframework.cloud.gateway.config.GatewayLoadBalancerClientAutoConfiguration
官方文档: