服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - SpringCloud Gateway 路由配置定位原理分析

SpringCloud Gateway 路由配置定位原理分析

2021-09-30 01:16mrr Java教程

本节主要了解系统中的谓词与配置的路由信息是如何进行初始化关联生成路由对象的。每个谓词工厂中的Config对象又是如何被解析配置的

环境:springcloud Hoxton.SR11

本节主要了解系统中的谓词与配置的路由信息是如何进行初始化关联生成路由对象的。每个谓词工厂中的Config对象又是如何被解析配置的。

所有的谓词工厂中的Config中属性值是如何被配置的。

SpringCloud Gateway中的所有谓词工厂如下:

SpringCloud Gateway 路由配置定位原理分析

命名规则:XxxRoutePredicateFactory。所有的这些谓词工厂都是如下的继承关系

  1. public class MethodRoutePredicateFactory extends AbstractRoutePredicateFactory<MethodRoutePredicateFactory.Config>
  2. //
  3. public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory<PathRoutePredicateFactory.Config>
  4. // ...

所有的谓词工厂继承的
AbstractRoutePredicateFactory中的泛型都是内部类的Config。这个是如何被配置上值的呢?

6.1 gateway自动配置

在下面这个类中配置了所有的Predicate和Filter。

  1. public class GatewayAutoConfiguration {
  2. @Bean
  3. @ConditionalOnEnabledPredicate
  4. public PathRoutePredicateFactory pathRoutePredicateFactory() {
  5. return new PathRoutePredicateFactory();
  6. }
  7. @Bean
  8. @ConditionalOnEnabledPredicate
  9. public QueryRoutePredicateFactory queryRoutePredicateFactory() {
  10. return new QueryRoutePredicateFactory();
  11. }
  12. @Bean
  13. public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, List<GatewayFilterFactory> gatewayFilters, List<RoutePredicateFactory> predicates, RouteDefinitionLocator routeDefinitionLocator, ConfigurationService configurationService) {
  14. return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates,
  15. gatewayFilters, properties, configurationService);
  16. }
  17. @Bean
  18. @Primary
  19. @ConditionalOnMissingBean(name = "cachedCompositeRouteLocator")
  20. public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
  21. return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
  22. }
  23. }

这里会层层委托最终查找查找路由定位会交给
RouteDefinitionRouteLocator。CachingRouteLocator起到缓存的作用,将配置的所有路由信息保存。

注意:这里的路由信息是在容器启动后就会被初始化的。

  1. public class CachingRouteLocator {
  2. private final RouteLocator delegate;
  3.  
  4. private final Flux<Route> routes;
  5.  
  6. private final Map<String, List> cache = new ConcurrentHashMap<>();
  7.  
  8. private ApplicationEventPublisher applicationEventPublisher;
  9.  
  10. public CachingRouteLocator(RouteLocator delegate) {
  11. this.delegate = delegate;
  12. routes = CacheFlux.lookup(cache, CACHE_KEY, Route.class) .onCacheMissResume(this::fetch);
  13. }
  14.  
  15. private Flux<Route> fetch() {
  16. return this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE);
  17. }
  18. }

实例化CachingRouteLocator就开始查找所有配置的Route信息。最终的会委托给
RouteDefinitionRouteLocator

RouteDefinitionRouteLocator构造函数中的initFactories方法用来映射路由工厂的XxxRoutePredicateFactory。

  1. private void initFactories(List<RoutePredicateFactory> predicates) {
  2. predicates.forEach(factory -> {
  3. String key = factory.name();
  4. if (this.predicates.containsKey(key)) {
  5. this.logger.warn("A RoutePredicateFactory named " + key + " already exists, class: " + this.predicates.get(key) + ". It will be overwritten.");
  6. }
  7. this.predicates.put(key, factory);
  8. });
  9. }

方法中解析每一个谓词工厂对应的名称然后缓存到predicates 集合中。

factory.name()方法解析谓词名称。

  1. default String name() {
  2. return NameUtils.normalizeRoutePredicateName(getClass());
  3. }

CachingRouteLocator是个缓存路由定位器,是个首选的RouteLocator(@Primary),这里将
RouteDefinitionRouteLocator进行了合并。

6.2 生成路由对象Route及Config配置

getRoutes---》convertToRoute---》combinePredicates---》lookup。

根据上面的自动配置也知道了在服务启动时就进行初始化所有路由信息了。

获取路由信息

  1. public Flux<Route> getRoutes() {
  2. Flux<Route> routes = this.routeDefinitionLocator.getRouteDefinitions() .map(this::convertToRoute);
  3. routes = routes.onErrorContinue((error, obj) -> {
  4. return routes.map(route -> {
  5. return route;
  6. });
  7. }

合并谓词

  1. private AsyncPredicate<ServerWebExchange> combinePredicates(
  2. RouteDefinition routeDefinition) {
  3. // other code
  4. for (PredicateDefinition andPredicate : predicates.subList(1, predicates.size())) {
  5. AsyncPredicate<ServerWebExchange> found = lookup(routeDefinition, andPredicate);
  6. predicate = predicate.and(found);
  7. }
  8. return predicate;
  9. }

进入lookup中

  1. private AsyncPredicate<ServerWebExchange> lookup(RouteDefinition route, PredicateDefinition predicate) {
  2. RoutePredicateFactory<Object> factory = this.predicates.get(predicate.getName());
  3. if (factory == null) {
  4. throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName());
  5. }
  6. // 这里将配置中(yml文件)配置的name,args和谓词工厂中的Config进行关联设置值
  7. Object config = this.configurationService.with(factory)
  8. .name(predicate.getName())
  9. .properties(predicate.getArgs())
  10. .eventFunction((bound, properties) -> new PredicateArgsEvent(
  11. RouteDefinitionRouteLocator.this, route.getId(), properties))
  12. .bind();
  13. // 最终调用谓词工厂(XxxRoutePredicateFactory的apply方法返回RoutePredicate该对象继承Predicate)
  14. return factory.applyAsync(config);
  15. }

lookup方法中查找,也就是在这里将对应的谓词Config与RouteDefinition(Predicate)中定义的相对应的属性关联。

进入factory.applyAsync方法

  1. @FunctionalInterface
  2. public interface RoutePredicateFactory<C> extends ShortcutConfigurable, Configurable<C> {
  3. default AsyncPredicate<ServerWebExchange> applyAsync(C config) {
  4. return toAsyncPredicate(apply(config)); // 查看下面的6.2-1图当前apply所有的实现就是系统内部定义的XxxRoutePredicateFactory
  5. }
  6. }
  7. // apply(config),如这里配置了Path谓词,那么就会进入PathRoutePredicateFactory中的apply方法
  8. public Predicate<ServerWebExchange> apply(Config config) {
  9. // other code
  10. return new GatewayPredicate() {
  11. public boolean test() {
  12. // todo
  13. }
  14. }
  15. }
  16. // 最后返回一个异步的谓词
  17. public static AsyncPredicate<ServerWebExchange> toAsyncPredicate(Predicate<? super ServerWebExchange> predicate) {
  18. Assert.notNull(predicate, "predicate must not be null");
  19. // 这里from就是返回一个DefaultAsyncPredicate默认的异步谓词
  20. return AsyncPredicate.from(predicate);
  21. }
  22. static AsyncPredicate<ServerWebExchange> from( Predicate<? super ServerWebExchange> predicate) {
  23. return new DefaultAsyncPredicate<>(GatewayPredicate.wrapIfNeeded(predicate));
  24. }

图6.2-1

SpringCloud Gateway 路由配置定位原理分析

最后在combinePredicates方法中将当前路由中配置的所有谓词进行了and操作返回。最终回到convertToRoute方法中将当前路由中配置的谓词,过滤器进行了整合包装返回Route(一个路由对象)

  1. public class Route implements Ordered {
  2. private final String id;
  3.  
  4. private final URI uri;
  5.  
  6. private final int order;
  7.  
  8. private final AsyncPredicate<ServerWebExchange> predicate;
  9.  
  10. private final List<GatewayFilter> gatewayFilters;
  11.  
  12. private final Map<String, Object> metadata;
  13. }

这些Route对象会被保存在上面说的
CachingRouteLocator.routes中。

6.3 定位路由

根据上面的配置RouteLocator 该类用来定位路由(查找具体的使用哪个路由);当一个请求过来会查找是哪个路由。

RouteLocator中定义了一个方法

  1. public interface RouteLocator {
  2.  
  3. Flux<Route> getRoutes();
  4.  
  5. }

查看这个getRoutes方法是谁调用的

SpringCloud Gateway 路由配置定位原理分析

看到这个
RoutePredicateHandlerMapping是不是想起了Spring MVC中的HandlerMapping(我们所有的Controller都会被RequestMappingHanlderMapping 匹配)。通过名称也就知道了该HandlerMapping用来匹配我们的路由谓词的谁来处理路由。

接下来回到前面说的
RequestMappingHanlderMapping 对象,当我们请求一个路由地址时会执行该类中的lookup方法查找路由

  1. protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
  2. // 这里的this.routeLocator就是 CachingRouteLocator对象
  3. return this.routeLocator.getRoutes()
  4. .concatMap(route -> Mono.just(route).filterWhen(r -> {
  5. exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
  6. // 过滤查找符合的路由
  7. return r.getPredicate().apply(exchange);
  8. }).doOnError(e -> logger.error(
  9. "Error applying predicate for route: " + route.getId(),
  10. e)).onErrorResume(e -> Mono.empty()))
  11. .next()
  12. .map(route -> {
  13. if (logger.isDebugEnabled()) {
  14. logger.debug("Route matched: " + route.getId());
  15. }
  16. validateRoute(route, exchange);
  17. return route;
  18. });
  19. }

进入r.getPredicate().apply(exchange)

  1. public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {
  2. static AsyncPredicate<ServerWebExchange> from(Predicate<? super ServerWebExchange> predicate) {
  3. return new DefaultAsyncPredicate<>(GatewayPredicate.wrapIfNeeded(predicate));
  4. }
  5.  
  6. class DefaultAsyncPredicate<T> implements AsyncPredicate<T> {
  7.  
  8. private final Predicate<T> delegate;
  9.  
  10. public DefaultAsyncPredicate(Predicate<T> delegate) {
  11. this.delegate = delegate;
  12. }
  13.  
  14. @Override
  15. public Publisher<Boolean> apply(T t) {
  16. return Mono.just(delegate.test(t));
  17. }
  18.  
  19. @Override
  20. public String toString() {
  21. return this.delegate.toString();
  22. }
  23.  
  24. }
  25.  
  26. }

这里会调用Predicate.test方法(XxxRoutePredicateFactory中的apply方法返回的GatewayPredicate)。

调用GatewayPredicate.test返回判断当前请求的路由是否匹配。

整体的一个流程:

1、系统先初始化所有的Predicate(谓词)和Filter(过滤器)

2、根据配置的路由信息(过滤器,谓词)包装返回Route对象

3、根据请求路由路径查找匹配的路由

完毕!!!

到此这篇关于SpringCloud Gateway 路由配置定位原理分析的文章就介绍到这了,更多相关SpringCloud Gateway原理分析内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

延伸 · 阅读

精彩推荐
  • Java教程Java实现抢红包功能

    Java实现抢红包功能

    这篇文章主要为大家详细介绍了Java实现抢红包功能,采用多线程模拟多人同时抢红包,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙...

    littleschemer13532021-05-16
  • Java教程xml与Java对象的转换详解

    xml与Java对象的转换详解

    这篇文章主要介绍了xml与Java对象的转换详解的相关资料,需要的朋友可以参考下...

    Java教程网2942020-09-17
  • Java教程Java BufferWriter写文件写不进去或缺失数据的解决

    Java BufferWriter写文件写不进去或缺失数据的解决

    这篇文章主要介绍了Java BufferWriter写文件写不进去或缺失数据的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望...

    spcoder14552021-10-18
  • Java教程升级IDEA后Lombok不能使用的解决方法

    升级IDEA后Lombok不能使用的解决方法

    最近看到提示IDEA提示升级,寻思已经有好久没有升过级了。升级完毕重启之后,突然发现好多错误,本文就来介绍一下如何解决,感兴趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程小米推送Java代码

    小米推送Java代码

    今天小编就为大家分享一篇关于小米推送Java代码,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧...

    富贵稳中求8032021-07-12
  • Java教程20个非常实用的Java程序代码片段

    20个非常实用的Java程序代码片段

    这篇文章主要为大家分享了20个非常实用的Java程序片段,对java开发项目有所帮助,感兴趣的小伙伴们可以参考一下 ...

    lijiao5352020-04-06
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    这篇文章主要介绍了Java使用SAX解析xml的示例,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程Java8中Stream使用的一个注意事项

    Java8中Stream使用的一个注意事项

    最近在工作中发现了对于集合操作转换的神器,java8新特性 stream,但在使用中遇到了一个非常重要的注意点,所以这篇文章主要给大家介绍了关于Java8中S...

    阿杜7472021-02-04