跳至主要內容

Spring Cloud Gateway 内置的全局过滤器(Global Filters)

悟空约 2186 字大约 7 分钟...

Spring Cloud Gateway 内置的全局过滤器(Global Filters)

本文基于 Spring Cloud Greenwich SR2

原文链接:https://www.jianshu.com/p/3ab97acf1e69open in new window

这里将 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 返回的 ServiceInstanceisSecure 的值,会覆盖请求的 scheme。举个例子,如果请求打到 Gateway 上使用的是 HTTPS ,但 ServiceInstanceisSecurefalse,那么下游微服务接收到的则是 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 是 httphttps ,那么该 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 的值就明白了,如下图:

img
img

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

如果你使用 SockJSopen in new window 作为普通 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.Seriesopen in new window 分类
  • status:返回给客户端的 Http Status
  • httpStatusCode:返回给客户端的请求的 Http Status
  • httpMethod:请求所使用的 Http 方法

这些指标暴露在 /actuator/metrics/gateway.requests 端点中,并且可以轻松与 Prometheus 整合,从而创建一个 Grafana dashboardopen in new window

注:Prometheus 是一款监控工具,Grafana 是一款监控可视化工具;Spring Boot Actuator 可与这两款工具进行整合。

9、Marking An Exchange As Routed

当一个请求走完整条过滤器链后,负责转发请求到下游的那个过滤器会在 exchange 中添加一个 gatewayAlreadyRouted 属性,从而将 exchange 标记为 routed(已路由)。一旦请求被标记为 routed ,其他路由过滤器将不会再次路由该请求,而是直接跳过。

了解了以上所有内置的全局过滤器后,我们知道不同协议的请求会由不同的过滤器转发到下游。所以负责添加这个gatewayAlreadyRouted 属性的过滤器就是最终负责转发请求的过滤器:

  • http、https 请求会由NettyRoutingFilterWebClientHttpRoutingFilter添加这个属性
  • 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

官方文档:

评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.3.0