本文基于Spring Cloud Greenwich SR2
[TOC]
这里将 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的值就明白了,如下图:
如果 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
:路由ID -
routeUri
:API将路由到的URI -
outcome
:由 HttpStatus.Series 分类 -
status
:返回给客户端的Http Status -
httpStatusCode
:返回给客户端的请求的Http Status -
httpMethod
:请求所使用的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
官方文档: