Spring Cloud Alibaba学习笔记(20) - Spring Cloud Gateway 内置的全局过滤器
参考:https://cloud.spring.io/spring-cloud-static/Greenwich.SR2/single/spring-cloud.html#_global_filters
全局过滤器 | 作用 |
---|---|
Combined Global Filter and GatewayFilter Ordering | 对过滤器执行顺序进行排序 |
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 | 整合监控相关,提供监控指标 |
Marking An Exchange As Routed | 防止重复的路由转发 |
Combined Global Filter and GatewayFilter Ordering
当Gateway接收到请求时,Filtering Web Handler 处理器会将所有的 GlobalFilter 实例以及所有路由上所配置的 GatewayFilter 实例添加到一条过滤器链中。该过滤器链里的所有过滤器都会按照 org.springframework.core.Ordered 注解所指定的数字大小进行排序。
Spring Cloud Gateway区分了过滤器逻辑执行的 ”pre” 和 ”post” 阶段,所以优先级高的过滤器将会在 “pre” 阶段最先执行,优先级最低的过滤器则在 “post” 阶段最后执行。
- 数字越小越靠前执行
示例代码:
@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
Forward Routing Filter
当请求进来时,ForwardRoutingFilter 会查看一个URL,该URL为 exchange 属性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的值,如果该 url 的 scheme 是 forward(例如:forward://localendpoint),那么该Filter会使用Spirngd的DispatcherHandler 来处理这个请求。该请求的URL路径部分,会被forward URL中的路径覆盖掉。而未修改过的原始URL,会被追加到 ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR 属性中。
PS:所谓 url scheme 简单来说就是 url 中的协议部分,例如http、https、ws等。自定义的 scheme 通常用于标识该url的行为,例如app开发中通常使用url scheme来跳转页面
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 的配置。
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)
Netty Write Response Filter
NettyWriteResponseFilter 用于将代理响应写回网关的客户端侧,所以该过滤器会在所有其他过滤器执行完成后才执行,并且执行的条件是 exchange 中 ServerWebExchangeUtils.CLIENT_RESPONSE_CONN_ATTR 属性的值不为空,该值为 Netty 的 Connection 实例。(有一个实验性的过滤器: WebClientWriteResponseFilter 可实现相同功能,但无需Netty)
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 属性中。
如果 URL 具有 scheme 前缀,例如 lb:ws://serviceid ,该 lb scheme将从URL中剥离,并放到 ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR 中,方便后面的过滤器使用。
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 。
示例配置:
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/**
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。
PS:Prometheus是一款监控工具,Grafana是一款监控可视化工具;Spring Boot Actuator可与这两款工具进行整合。
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