点击上方"IT牧场",选择"设为星标"技术干货每日送达!
TIPS
本文基于Spring Cloud Gateway SR2,理论适配Spring Cloud Gateway SR1以及更高版本。
本文详细探讨Spring Cloud Gateway内置的全局过滤器。包括:
•Combined Global Filter and GatewayFilter Ordering•Forward Routing Filter•LoadBalancerClient Filter•Netty Routing Filter•Netty Write Response Filter•RouteToRequestUrl Filter•Websocket Routing Filter•Gateway Metrics Filter•Marking An Exchange As Routed
GlobalFilter
接口和 GatewayFilter
有一样的接口定义,只不过, GlobalFilter
会作用于所有路由。
TIPS
官方声明:GlobalFilter的接口定义以及用法在未来的版本可能会发生变化。
个人判断:GlobalFilter可用于生产;如果有自定义GlobalFilter的需求,理论上也可放心使用——未来即使接口定义以及使用方式发生变化,应该也是平滑过渡的(比如Zuul的Fallback,原先叫ZuulFallbackProvider,后来改叫FallbackProvider,中间就有段时间新旧使用方式都支持,后面才逐步废弃老的使用方式)。
当请求到来时,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
ForwardRoutingFilter
会查看exchange的属性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
的值(一个URI),如果该值l的scheme是 forward
,比如:forward://localendpoint
,则它会使用Spirng的DispatcherHandler
处理该请求。请求URL的路径部分,会被forward URL中的路径覆盖。未修改的原始URL,会被追加到 ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR
属性中。
TIPS
这段文档太学术了,讲解了
LoadBalancerClientFilter
的实现原理,对使用者来说,意义不大;对使用者来说,只要知道这个Filter是用来做本地forward就OK了。建议:如对原理感兴趣的,建议直接研究源码,源码比官方文档好理解。
LoadBalancerClientFilter
会查看exchange的属性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
的值(一个URI),如果该值的scheme是 lb
,比如:lb://myservice
,它将会使用Spring Cloud的LoadBalancerClient
来将 myservice
解析成实际的host和port,并替换掉 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
的内容。原始地址会追加到 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(对应如上的例子,找不到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
这段文档太学术了,讲解了
LoadBalancerClientFilter
的实现原理,对使用者来说,意义不大;对使用者来说,其实只要知道这个Filter是用来整合Ribbon的就OK了。建议:如对原理感兴趣的,建议直接研究源码,源码比官方文档好理解。
如果 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
的值的scheme是 http
或 https
,则运行Netty Routing Filter 。它使用Netty HttpClient
向下游发送代理请求。获得的响应将放在exchange的ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR
属性中,以便在后面的filter中使用。(有一个实验性的过滤器: WebClientHttpRoutingFilter
可实现相同功能,但无需Netty)
如果exchange中的 ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR
属性中有 HttpClientResponse
,则运行 NettyWriteResponseFilter
。该过滤器在所有其他过滤器执行完成后执行,并将代理响应协会网关的客户端侧。(有一个实验性的过滤器: WebClientWriteResponseFilter
可实现相同功能,但无需Netty)
如果exchange中的ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR
属性中有一个 Route
对象,则运行 RouteToRequestUrlFilter
。它根据请求URI创建一个新URI,但会使用该 Route
对象的URI属性进行更新。新URI放到exchange的 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
属性中。
如果URI具有scheme前缀,例如 lb:ws://serviceid
,该 lb
scheme将从URI中剥离,并放到 ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR
中,方便后面的过滤器使用。
如果exchange中的 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR
属性的值的scheme是 ws
或者 wss
,则运行Websocket Routing Filter。它底层使用Spring Web Socket将Websocket请求转发到下游。
可为URI添加 lb
前缀实现负载均衡,例如 lb:ws://serviceid
。
如果你使用 SockJS[1] 所谓普通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/**
要启用Gateway Metrics,需添加 spring-boot-starter-actuator
依赖。然后,只要spring.cloud.gateway.metrics.enabled
的值不是false,就会运行Gateway Metrics Filter。此过滤器添加名为 gateway.requests
的时序度量(timer metric),其中包含以下标记:
•routeId
:路由ID•routeUri
:API将路由到的URI•outcome
:由 HttpStatus.Series[2] 分类•status
:返回给客户端的Http Status•httpStatusCode
:返回给客户端的请求的Http Status•httpMethod
:请求所使用的Http方法
这些指标暴露在 /actuator/metrics/gateway.requests
端点中,并且可以轻松与Prometheus整合,从而创建一个 Grafana[3] dashboard[4] 。
TIPS
Prometheus是一款监控工具,Grafana是一款监控可视化工具;Spring Boot Actuator可与这两款工具进行整合。关于整合,笔者写过手把手的博客,有兴趣可以看一下:
Spring Boot 2.x监控数据可视化(Actuator + Prometheus + Grafana手把手)[5]
在网关路由 ServerWebExchange
后,它将通过在exchange添加一个 gatewayAlreadyRouted
属性,从而将exchange标记为 routed
。一旦请求被标记为 routed
,其他路由过滤器将不会再次路由请求,而是直接跳过。您可以使用便捷方法将exchange标记为 routed
,或检查exchange是否是 routed
。
•ServerWebExchangeUtils.isAlreadyRouted
检查是否已被路由•ServerWebExchangeUtils.setAlreadyRouted
设置routed状态
TIPS
简单来说,就是网关通过
gatewayAlreadyRouted
属性表示这个请求已经转发过了,而无需其他过滤器重复路由。从而防止重复的路由操作。
•114. Global Filters[6]
最近将个人学习笔记整理成册,使用PDF分享。关注我,回复如下代码,即可获得百度盘地址,无套路领取!
•001:《Java并发与高并发解决方案》学习笔记;•002:《深入JVM内核——原理、诊断与优化》学习笔记;•003:《Java面试宝典》•004:《Docker开源书》•005:《Kubernetes开源书》•006:《DDD速成(领域驱动设计速成)》•007:全部•008:加技术讨论群
•Code Review最佳实践•Spring Cloud Gateway-过滤器工厂详解(GatewayFilter Factories)•Spring Cloud Gateway-路由谓词工厂详解(Route Predicate Factories)•细说 Java 主流日志工具库•亚马逊实践领域驱动设计之道•秒懂 QPS、TPS、PV、UV、GMV、IP、RPS!
[1]
SockJS: https://github.com/sockjs[2]
HttpStatus.Series: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/HttpStatus.Series.html[3]
Grafana: https://cloud.spring.io/spring-cloud-gateway/reference/html/images/gateway-grafana-dashboard.jpeg[4]
dashboard: https://cloud.spring.io/spring-cloud-gateway/reference/html/gateway-grafana-dashboard.json[5]
Spring Boot 2.x监控数据可视化(Actuator + Prometheus + Grafana手把手): http://www.itmuch.com/spring-boot/actuator-prometheus-grafana/[6]
114. Global Filters: https://cloud.spring.io/spring-cloud-static/Greenwich.SR2/single/spring-cloud.html#_global_filters