spring gateway 如何为请求路由到“路由策略”

这次先不聊 SAAS 也不聊教育,先聊聊实际的问题:

先回答标题:由 RouteDefinitionRouteLocator 实现路由策略的管理,并提供检索

RouteDefinitionRouteLocator 在 其构造方法中会加载所有的路由器(称 Routers),这些路由器都是以 filter (GatewayFilter)的形式工作的, 但是这些 filter 它不是直接写好的类, 而是由工厂生产出来例如:

RewritePathGatewayFilterFactory, 通过 apply 方法生产出一个 GatewayFilter,其内部实现了重写 path 的逻辑,再例如:

RequestHeaderToRequestUriGatewayFilterFactory, 通过 apply 方法生产出来一个能通过地址路由到新地址的 GatewayFilter。

所以 RouteDefinitionRouteLocator 中维护的其实是一组 GatewayFilter 的各种工厂(工厂模式)。

可以读下原码:

public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,
      List<RoutePredicateFactory> predicates,
      List<GatewayFilterFactory> gatewayFilterFactories,
      GatewayProperties gatewayProperties,
      ConfigurationService configurationService) {
   this.routeDefinitionLocator = routeDefinitionLocator;
   this.configurationService = configurationService;
   initFactories(predicates);
   gatewayFilterFactories.forEach(
         factory -> this.gatewayFilterFactories.put(factory.name(), factory));
   this.gatewayProperties = gatewayProperties;
}

它将 List<GatewayFilterFactory> gatewayFilterFactories, 利用 map 建了索引 (Map<String, GatewayFilterFactory> gatewayFilterFactories)。后面再看看它是如何使用的。

为何标题叫 : 路由到“路由策略”?

因为当请求过来的时候 gateway 会根据请求特征,先找到这个请求需要有哪些 GatewayFilter 来加工它,所以它得先从这一众 GatewayFilterFactory 中找到这些目标工厂,最终得到的是一组 GatewayFilterFactory 集合(只要调用它们的 apply 方法就能得到对应的 Filter),所以我称这组集合为 “路由策略” ,然后不用想这组策略应该会被交给 一个 FilterChain 去执行,执行的是对我们原请求的一系列路由管理策略例如 把路径 /xx 的请求转发到 /xxxzzz,把路径 /ssss 替换成 /yyy,或者是其他的自定义路由器。这是它的原理,我们可以看看 RouteDefinitionRouteLocator.getRoutes 方法(主要看 loadGatewayFilters):

@Override
public Flux<Route> getRoutes() {
   return this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute)
         // TODO: error handling
         .map(route -> {
            if (logger.isDebugEnabled()) {
               logger.debug("RouteDefinition matched: " + route.getId());
            }
            return route;
         });

   /*
    * TODO: trace logging if (logger.isTraceEnabled()) {
    * logger.trace("RouteDefinition did not match: " + routeDefinition.getId()); }
    */
}
private Route convertToRoute(RouteDefinition routeDefinition) {
   AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
   List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);

   return Route.async(routeDefinition).asyncPredicate(predicate)
         .replaceFilters(gatewayFilters).build();
}
private AsyncPredicate<ServerWebExchange> combinePredicates(
      RouteDefinition routeDefinition) {
   List<PredicateDefinition> predicates = routeDefinition.getPredicates();
   AsyncPredicate<ServerWebExchange> predicate = lookup(routeDefinition,
         predicates.get(0));

   for (PredicateDefinition andPredicate : predicates.subList(1,
         predicates.size())) {
      AsyncPredicate<ServerWebExchange> found = lookup(routeDefinition,
            andPredicate);
      predicate = predicate.and(found);
   }

   return predicate;
}
private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
   List<GatewayFilter> filters = new ArrayList<>();

   // TODO: support option to apply defaults after route specific filters?
   if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
      filters.addAll(loadGatewayFilters(DEFAULT_FILTERS,
            this.gatewayProperties.getDefaultFilters()));
   }

   if (!routeDefinition.getFilters().isEmpty()) {
      filters.addAll(loadGatewayFilters(routeDefinition.getId(),
            routeDefinition.getFilters()));
   }

   AnnotationAwareOrderComparator.sort(filters);
   return filters;
}
@SuppressWarnings("unchecked")
List<GatewayFilter> loadGatewayFilters(String id,
      List<FilterDefinition> filterDefinitions) {
   ArrayList<GatewayFilter> ordered = new ArrayList<>(filterDefinitions.size());
   for (int i = 0; i < filterDefinitions.size(); i++) {
      FilterDefinition definition = filterDefinitions.get(i);
      GatewayFilterFactory factory = this.gatewayFilterFactories
            .get(definition.getName());
      if (factory == null) {
         throw new IllegalArgumentException(
               "Unable to find GatewayFilterFactory with name "
                     + definition.getName());
      }
      if (logger.isDebugEnabled()) {
         logger.debug("RouteDefinition " + id + " applying filter "
               + definition.getArgs() + " to " + definition.getName());
      }

      // @formatter:off
      Object configuration = this.configurationService.with(factory)
            .name(definition.getName())
            .properties(definition.getArgs())
            .eventFunction((bound, properties) -> new FilterArgsEvent(
                  // TODO: why explicit cast needed or java compile fails
                  RouteDefinitionRouteLocator.this, id, (Map<String, Object>) properties))
            .bind();
      // @formatter:on

      // some filters require routeId
      // TODO: is there a better place to apply this?
      if (configuration instanceof HasRouteId) {
         HasRouteId hasRouteId = (HasRouteId) configuration;
         hasRouteId.setRouteId(id);
      }

      GatewayFilter gatewayFilter = factory.apply(configuration);
      if (gatewayFilter instanceof Ordered) {
         ordered.add(gatewayFilter);
      }
      else {
         ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
      }
   }

   return ordered;
}

loadGatewayFilters 是从预建的 map 索引中,根据 id 来路由到对应的 factory, 再调 factory.apply 方法得到这个 Filter,然后添加到 ArrayList<GatewayFilter> ordered 中, 这 ordered 就是我们本文的核心 :路由策略。

小结下:通过初始化的时候加载到所有的工厂,并按服务名称建议工厂索引,用到它的时候,再通过 getRoutes 方法从索引中取出路由策略,然后交给 FilterChain。涉及到的设计模式:工厂模式、责任链、面向接口的编程思想,还可以把 react 也提下,可能还有建造者模式。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爬上树顶

打赏可验证我能否靠此文财务自由

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值