1. 前言
今天学习一下Spring Cloud Gateway,就先再其他博客上逛了逛。遇到有java开发者在某博客问一个问题:Spring Cloud Gateway 如何添加统一的前缀? 当时没有在意,但是脑子里也带着这个问题看起了文档。随着慢慢了解Spring Cloud Gateway 这个问题就有了一点思路。
2.Gateway工作机制
这是官方文档上给的Spring Cloud Gateway工作流程图。大意上是客户端请求经过HandlerMapping的处理,如果匹配到路由(Router)就交给网关的web处理程序(Gateway Web Handler)来处理,经过一系列的调用过滤器链(肯定有责任链模式)后转发到被代理的服务执行真正的调用逻辑。
3. Gateway Handler Mapping
根据上图,我想找到所谓的Gateway Handler Mapping,看看是何方神圣。我找到了RoutePredicateHandlerMapping,并确定该类就是那个handler Mapping。依赖于Spring�º�,ů�� Webflux响应式web编程模型。核心方法是getHandlerInternal,通过该方法进行内部处理。
从代码上看 就是处理了端口关系后,在request放入一些gateway特定的attribute。然后走的一个寻找路由的方法。最后通过Mono.just(webHandler)交给了一个FilteringWebHandler类型的webHandler来处理了。我们先来关注lookupRoute方法,该方法摆明了就是寻找路由。该方法的代码不贴了。就是通过routeLocator.getRoutes()来加载所有的路由并通过断言(Predicate)来进行匹配。匹配到就交给FilteringWebHandler 走过滤器链。在我明白这个流程之后,脑子里那个问题就有了眉目。
4. FilteringWebHandler
该handler就是图中的Gateway Web Handler ,包含了一系列的GlobalFilter和GatewayFilter 来组成一个chain来处理前置和后置的逻辑,然后交给具体代理的服务。
5. 增加统一前缀的思路
在以前我们知道zuul网关是可以添加一个统一前缀的。但是Spring Cloud Gateway是没有直接提供这个功能的。我已经搞清楚了Gateway的大致工作机制,甚至是一些细节。首先这个统一前缀肯定不能在断言中处理。断言是根据请求的个性化来找目的地路由的,而统一前缀是共性的。放在断言执行后也就是FilteringWebHandler来处理就更不合适了。因为断言执行在Filter之前。
有一个东西我们没有注意到。Spring Cloud Gateway的机制依赖于Spring Webflux框架的。经过查一些资料RoutePredicateHandlerMapping处理之前是可以设置WebFilter的。因此我找到了一个解决方案:在Gateway Client 和 Gateway Handler Mapping 之间定制一个WebFilter来处理统一前缀。
6. 思路实现
机制是这样的,请求带统一前缀请求经过我定制的WebFilter去掉前缀(当然你可以加一些其他你需要的逻辑)然后交给Gateway Handler Mapping处理。代码如下:
经过测试有效。
7. 总结
这里其实重点不是如何来实现这个功能,我想传达的是一个解决问题的思路。如何从框架的工作机制出发来分析你所需要解决的问题。找到那个合适的切入点。思路清晰了对于解决问题来说至关重要。其实我现在对Spring Cloud Gateway和Spring Webflux 都并不是很熟练,但是依然找了一个解决问题的方法。不知道你有没有更好的方法呢?相关代码已经上传到码云仓库:
https://gitee.com/felord/tino-cloud.git