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

本文基于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 返回的 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的值就明白了,如下图:


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:路由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请求会由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

官方文档:

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • 花线(蚕丝花线) 又称绣花线,以蚕丝纺成的细线为主,是构成绣品的基本材料。蚕丝花线的内部结构,以“绞、支、根、绒、...
    石生花之鹿阅读 1,274评论 0 0
  • 嘿嘿,以后只要想玩宝宝巴士必须先做十道数学题,或者写一张生字 写的歪歪扭扭
    是军儿呀阅读 194评论 0 0
  • 今天坐着看书,一个蚊子咬了好多口,于是站了起来走了几步,没想到蚊子跟着我飞了过来,我身上除了雄性荷尔蒙也没有其它味...
    qiekeqieke阅读 353评论 0 1
  • 又到了每年一度的鼓楼舞蹈灯光之夜了,童年有记忆起,最开心的1天就是这天了。整条街都是欢声笑语跳着各种舞蹈的人们,不...
    啊木Blue阅读 211评论 0 0
  • 有你在身边, 我眼里满是欢喜。 有你在身边, 风吹过来都是甜的。 有你在身边, 连花儿都开的别样红。 有你在身边,...
    七根阅读 297评论 0 1