关闭

深入SpringCloud底层详解路由配置定位原理

发表于:2023-7-24 09:18

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:Springboot实战案例锦    来源:实战案例锦集

  环境:SpringCloud Hoxton.SR11
  本节主要了解系统中的谓词与配置的路由信息是如何进行初始化关联生成路由对象的。每个谓词工厂中的Config对象又是如何被解析配置的。
  所有的谓词工厂中的Config中属性值是如何被配置的。
  在SpringCloud Gateway中的所有谓词工厂如下:
  命名规则:XxxRoutePredicateFactory。所有的这些谓词工厂都是如下的继承关系
  public class MethodRoutePredicateFactory extends   AbstractRoutePredicateFactory<MethodRoutePredicateFactory.Config>
  // 
  public class PathRoutePredicateFactory extends AbstractRoutePredicateFactory<PathRoutePredicateFactory.Config>
  // ...
  所有的谓词工厂继承的AbstractRoutePredicateFactory中的泛型都是内部类的Config。这个是如何被配置上值的呢?
  6.1 gateway自动配置
  在下面这个类中配置了所有的Predicate和Filter。
  public class GatewayAutoConfiguration {
    @Bean
    @ConditionalOnEnabledPredicate
    public PathRoutePredicateFactory pathRoutePredicateFactory() {
      return new PathRoutePredicateFactory();
    }
    @Bean
    @ConditionalOnEnabledPredicate
    public QueryRoutePredicateFactory queryRoutePredicateFactory() {
      return new QueryRoutePredicateFactory();
    }
    @Bean
    public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, List<GatewayFilterFactory> gatewayFilters, List<RoutePredicateFactory> predicates, RouteDefinitionLocator routeDefinitionLocator, ConfigurationService configurationService) {
      return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates,
              gatewayFilters, properties, configurationService);
    }
    @Bean
    @Primary
    @ConditionalOnMissingBean(name = "cachedCompositeRouteLocator")
    public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
      return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
    }
  }
  这里会层层委托最终查找查找路由定位会交给RouteDefinitionRouteLocator。CachingRouteLocator起到缓存的作用,将配置的所有路由信息保存。
  注意:这里的路由信息是在容器启动后就会被初始化的。
  public class CachingRouteLocator {
    private final RouteLocator delegate;
    private final Flux<Route> routes;
    private final Map<String, List> cache = new ConcurrentHashMap<>();
    private ApplicationEventPublisher applicationEventPublisher;
    public CachingRouteLocator(RouteLocator delegate) {
      this.delegate = delegate;
      routes = CacheFlux.lookup(cache, CACHE_KEY, Route.class) .onCacheMissResume(this::fetch);
    }
    private Flux<Route> fetch() {
      return this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE);
    }
  }
  实例化CachingRouteLocator就开始查找所有配置的Route信息。最终的会委托给RouteDefinitionRouteLocator
  RouteDefinitionRouteLocator构造函数中的initFactories方法用来映射路由工厂的XxxRoutePredicateFactory。
  private void initFactories(List<RoutePredicateFactory> predicates) {
    predicates.forEach(factory -> {
      String key = factory.name();
      if (this.predicates.containsKey(key)) {
        this.logger.warn("A RoutePredicateFactory named " + key + " already exists, class: " + this.predicates.get(key) + ". It will be overwritten.");
      }
      this.predicates.put(key, factory);
    });
  }
  方法中解析每一个谓词工厂对应的名称然后缓存到predicates 集合中。
  factory.name()方法解析谓词名称。
  default String name() {
    return NameUtils.normalizeRoutePredicateName(getClass());
  }
  CachingRouteLocator是个缓存路由定位器,是个首选的RouteLocator(@Primary),这里将RouteDefinitionRouteLocator进行了合并。
  生成路由对象Route及Config配置
  getRoutes---》convertToRoute---》combinePredicates---》lookup。
  根据上面的自动配置也知道了在服务启动时就进行初始化所有路由信息了。
  获取路由信息
  public Flux<Route> getRoutes() {
    Flux<Route> routes = this.routeDefinitionLocator.getRouteDefinitions() .map(this::convertToRoute);
    routes = routes.onErrorContinue((error, obj) -> {
      return routes.map(route -> {
              return route;
    });
  }
  合并谓词
  private AsyncPredicate<ServerWebExchange> combinePredicates(
              RouteDefinition routeDefinition) {
    // other code
    for (PredicateDefinition andPredicate : predicates.subList(1, predicates.size())) {
      AsyncPredicate<ServerWebExchange> found = lookup(routeDefinition, andPredicate);
      predicate = predicate.and(found);
    }
    return predicate;
  }
  进入lookup中
  private AsyncPredicate<ServerWebExchange> lookup(RouteDefinition route, PredicateDefinition predicate) {
    RoutePredicateFactory<Object> factory = this.predicates.get(predicate.getName());
    if (factory == null) {
      throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName());
    }
    // 这里将配置中(yml文件)配置的name,args和谓词工厂中的Config进行关联设置值
    Object config = this.configurationService.with(factory)
      .name(predicate.getName())
      .properties(predicate.getArgs())
      .eventFunction((bound, properties) -> new PredicateArgsEvent(
          RouteDefinitionRouteLocator.this, route.getId(), properties))
      .bind();
    // 最终调用谓词工厂(XxxRoutePredicateFactory的apply方法返回RoutePredicate该对象继承Predicate)
    return factory.applyAsync(config);
  }
  lookup方法中查找,也就是在这里将对应的谓词Config与RouteDefinition(Predicate)中定义的相对应的属性关联。
  进入factory.applyAsync方法
  @FunctionalInterface
  public interface RoutePredicateFactory<C> extends ShortcutConfigurable, Configurable<C> {
    default AsyncPredicate<ServerWebExchange> applyAsync(C config) {
      return toAsyncPredicate(apply(config)); // 查看下面的6.2-1图当前apply所有的实现就是系统内部定义的XxxRoutePredicateFactory
    }
  }
  // apply(config),如这里配置了Path谓词,那么就会进入PathRoutePredicateFactory中的apply方法
  public Predicate<ServerWebExchange> apply(Config config) {
    // other code    
    return new GatewayPredicate() {
      public boolean test() {
        // todo    
      }
    }
  }
  // 最后返回一个异步的谓词
  public static AsyncPredicate<ServerWebExchange> toAsyncPredicate(Predicate<? super ServerWebExchange> predicate) {
    Assert.notNull(predicate, "predicate must not be null");
    // 这里from就是返回一个DefaultAsyncPredicate默认的异步谓词
    return AsyncPredicate.from(predicate);
  }
  static AsyncPredicate<ServerWebExchange> from( Predicate<? super ServerWebExchange> predicate) {
    return new DefaultAsyncPredicate<>(GatewayPredicate.wrapIfNeeded(predicate));
  }
  最后在combinePredicates方法中将当前路由中配置的所有谓词进行了and操作返回。最终回到convertToRoute方法中将当前路由中配置的谓词,过滤器进行了整合包装返回Route(一个路由对象)
  public class Route implements Ordered {
    private final String id;
     
    private final URI uri;
     
    private final int order;
     
    private final AsyncPredicate<ServerWebExchange> predicate;
     
    private final List<GatewayFilter> gatewayFilters;
     
    private final Map<String, Object> metadata;
  }
  这些Route对象会被保存在上面说的CachingRouteLocator.routes中。
  定位路由
  根据上面的配置RouteLocator 该类用来定位路由(查找具体的使用哪个路由);当一个请求过来会查找是哪个路由。
  RouteLocator中定义了一个方法
  public interface RouteLocator {
    Flux<Route> getRoutes();
  }
  查看这个getRoutes方法是谁调用的
  看到这个RoutePredicateHandlerMapping是不是想起了Spring MVC中的HandlerMapping(我们所有的Controller都会被 RequestMappingHanlderMapping 匹配)。通过名称也就知道了该HandlerMapping用来匹配我们的路由谓词的谁来处理路由。
  接下来回到前面说的RequestMappingHanlderMapping 对象,当我们请求一个路由地址时会执行该类中的lookup方法查找路由。
  protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
    // 这里的this.routeLocator就是 CachingRouteLocator对象 
    return this.routeLocator.getRoutes()
        .concatMap(route -> Mono.just(route).filterWhen(r -> {
          exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
          // 过滤查找符合的路由  
          return r.getPredicate().apply(exchange);
       }).doOnError(e -> logger.error(
            "Error applying predicate for route: " + route.getId(),
       e)).onErrorResume(e -> Mono.empty()))
          .next()
          .map(route -> {
            if (logger.isDebugEnabled()) {
              logger.debug("Route matched: " + route.getId());
            }
            validateRoute(route, exchange);
            return route;
       });
  }
  进入r.getPredicate().apply(exchange)
  public interface AsyncPredicate<T> extends Function<T, Publisher<Boolean>> {
    static AsyncPredicate<ServerWebExchange> from(Predicate<? super ServerWebExchange> predicate) {
    return new DefaultAsyncPredicate<>(GatewayPredicate.wrapIfNeeded(predicate));
    }
    class DefaultAsyncPredicate<T> implements AsyncPredicate<T> {
      private final Predicate<T> delegate;
      public DefaultAsyncPredicate(Predicate<T> delegate) {
        this.delegate = delegate;
      }
      @Override
      public Publisher<Boolean> apply(T t) {
        return Mono.just(delegate.test(t));
      }
      @Override
      public String toString() {
        return this.delegate.toString();
      }
    }
  }
  这里会调用Predicate.test方法(XxxRoutePredicateFactory中的apply方法返回的GatewayPredicate)。
  调用GatewayPredicate.test返回判断当前请求的路由是否匹配。
  整体的一个流程:
  1、系统先初始化所有的Predicate(谓词)和Filter(过滤器)
  2、根据配置的路由信息(过滤器,谓词)包装返回Route对象
  3、根据请求路由路径查找匹配的路由
  本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号