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

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

[[409660]]

 环境: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.   } 

这里会层层委托最终查找查找路由定位会交给

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.   } 

实例化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.   }); 

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

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

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

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.   }); 

合并谓词

  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; 

进入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); 

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. // apply(config),如这里配置了Path谓词,那么就会进入PathRoutePredicateFactory中的apply方法 
  7. public Predicate<ServerWebExchange> apply(Config config) { 
  8.   // other code     
  9.   return new GatewayPredicate() { 
  10.     public boolean test() { 
  11.       // todo     
  12.     } 
  13.   } 
  14. // 最后返回一个异步的谓词 
  15. public static AsyncPredicate<ServerWebExchange> toAsyncPredicate(Predicate<? super ServerWebExchange> predicate) { 
  16.   Assert.notNull(predicate, "predicate must not be null"); 
  17.   // 这里from就是返回一个DefaultAsyncPredicate默认的异步谓词 
  18.   return AsyncPredicate.from(predicate); 
  19. static AsyncPredicate<ServerWebExchange> from( Predicate<? super ServerWebExchange> predicate) { 
  20.   return new DefaultAsyncPredicate<>(GatewayPredicate.wrapIfNeeded(predicate)); 
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.  

查看这个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.      }); 

进入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.  

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

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

整体的一个流程:

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

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

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

 

责任编辑:姜华 来源: 今日头条
相关推荐

2023-07-20 10:04:37

底层路由配置

2023-08-11 08:42:49

泛型工厂继承配置

2021-03-26 06:01:45

日志MongoDB存储

2023-05-04 08:09:33

serviceId路径谓词中心注册

2019-09-10 13:58:57

动态路由路由器网络

2009-12-23 17:33:02

光纤路由技术

2010-07-08 15:08:12

2022-05-16 08:22:11

网关过滤器路由

2022-06-12 07:27:39

微服务GateWay

2022-05-12 08:21:13

项目网关模块

2020-11-18 11:26:45

SpringCloudZuulJava

2021-08-26 11:52:32

FeignWeb服务

2009-12-15 13:35:18

路由协议分析仪

2010-10-14 14:46:23

无线路由器配置

2009-12-09 10:07:22

配置静态路由

2023-10-16 22:07:20

Spring配置中心Bean

2009-11-11 14:18:00

动态路由协议

2010-01-22 17:56:31

千兆路由交换机

2009-12-17 14:07:58

配置静态路由

2009-12-11 15:41:16

路由交换机配置
点赞
收藏

51CTO技术栈公众号