Spring Cloud Gateway源码系列之路由配置加载过程

当前章节主要是讲解配置文件中定义的路由配置被gateway加载,同时转为可以直接操作的路由对象

引入pom坐标

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

前提:需要对springboot的自动装配要非常的熟悉,还有对reactor编程有一些了解,在分析过程中会对reactor一些基础进行讲一下

1. 找到组件加载入口spring.factories

在包spring-cloud-gateway-server包下,找到spring.factories文件(springboot的自动装配)
在这里插入图片描述

2. GatewayAutoConfiguration 自动装配类

最主要的装配类为GatewayAutoConfiguration

3. 加载配置文件中路由信息

@Bean
public GatewayProperties gatewayProperties() {
   return new GatewayProperties();
}

4. GatewayProperties对象

我们在配置文件中配置的路由规则会在启动的时候都加载到该对象中

@ConfigurationProperties(GatewayProperties.PREFIX)
@Validated
public class GatewayProperties {
   public static final String PREFIX = "spring.cloud.gateway";
   //路由配置
   private List<RouteDefinition> routes = new ArrayList<>();  
   //默认的过滤器
   private List<FilterDefinition> defaultFilters = new ArrayList<>(); 
   private List<MediaType> streamingMediaTypes = Arrays
         .asList(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_STREAM_JSON);
   private boolean failOnRouteDefinitionError = true;
   // 统计配置
   private Metrics metrics = new Metrics();
}

5. RouteDefinition每个路由的详细配置

路由的格式

spring:
  cloud:
    gateway:
      routes:
        - id: test
          uri: http://localhost:8081
          predicates:
            - Path=/test/**
        - id: test2
          uri: http://localhost:8082
          predicates:
            - Path=/api/**

对应的路由配置

public class RouteDefinition {
    // 路由唯一标识
   private String id;
   // 路由规则
   private List<PredicateDefinition> predicates = new ArrayList<>();
   // 指定路由的过滤器
   private List<FilterDefinition> filters = new ArrayList<>();
   // 路由匹配后请求的地址
   private URI uri;
   // 元数据
   private Map<String, Object> metadata = new HashMap<>();
   // 加载序列
   private int order = 0;
}

6. 将路由的定义信息转为路由控制对象

在装配类GatewayAutoConfiguration中找到一下装配对象

@Bean
public RouteLocator routeDefinitionRouteLocator(
      GatewayProperties properties, // 路由定义信息
      List<GatewayFilterFactory> gatewayFilters, // 过滤器,之后的章节中进行分析
      List<RoutePredicateFactory> predicates,  //路由器匹配规则
      RouteDefinitionLocator routeDefinitionLocator, //路由定义信息加载器
      ConfigurationService configurationService) {
   return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates,
         gatewayFilters, properties, configurationService);
}

7. RouteDefinitionRouteLocator对象实例化构建

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

a. initFactories(predicates) 初始化路由匹配工厂

主要是将路由匹配规则转换为断言对象,用来对每个请求的url进行判断是否使用该规则下的路由地址

private void initFactories(List<RoutePredicateFactory> predicates) {
   predicates.forEach(factory -> {
      String key = factory.name();
      if (this.predicates.containsKey(key)) {
         //打印日志
      }
      this.predicates.put(key, factory);
   });
}

this.predicates的存储最后结果如下图,加载多种路由工厂
在这里插入图片描述
在我们的配置匹配规则时,会有类似于Path=/test/**前面的Path或者其它的,它是来决定使用哪个工厂来生成断言对象的。不同的规则有不同的方式。具体的路由匹配规则在下一章进行分析。

b. gatewayFilterFactories.forEach( factory -> this.gatewayFilterFactories.put(factory.name(), factory));

加载所有可以选择的过滤器

spring:
  cloud:
    gateway:
      routes:
        - id: test
          uri: http://localhost:8081
          predicates:
            - Path=/test/**
            - Query=param, value
          filters: 过滤器
            - AddRequestHeader=key,value

加载过滤器,gatewayFilterFactories是自动装配的bean,也都在GatewayAutoConfiguration中进行装配,gateway提供了很多可以在路由配置加的过滤器,例如:AddRequestHeader=key,value。
在这里插入图片描述

8. RouteDefinitionRouteLocator对象中getRoutes() 获取路由器

a. 这个方法主要上将配置文件中的路由定义信息转为可操作的路由对象

@Override
public Flux<Route> getRoutes() {
   // 通过定义信息转为路由对象集Route
   // routeDefinitionLocator.getRouteDefinitions路由的定义信息
   // 重点在convertToRoute方法中
   Flux<Route> routes = this.routeDefinitionLocator.getRouteDefinitions()
         .map(this::convertToRoute);
    // 路由配置错误是否只是打印日志
   if (!gatewayProperties.isFailOnRouteDefinitionError()) {
      // instead of letting error bubble up, continue
      routes = routes.onErrorContinue((error, obj) -> {
         if (logger.isWarnEnabled()) {
            logger.warn("RouteDefinition id " + ((RouteDefinition) obj).getId()
                  + " will be ignored. Definition has invalid configs, "
                  + error.getMessage());
         }
      });
   }
   // 打印配置的所有路由ID日志
   return routes.map(route -> {
      if (logger.isDebugEnabled()) {
         logger.debug("RouteDefinition matched: " + route.getId());
      }
      return route;
   });
}

Flux可以理解为java8中提供的Stream类,包含的是0-n个对象,可以对包含的对象做批量处理,像在Stream中一样做过滤、排序、映射…

b. Route对象

public class Route implements Ordered {
   // 路由ID
   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;
}

predicate对象实际上就是一组规约对象,对一次请求进行判断是否使用该路由,这个对象实际上是包含一组,我们在配置路由规则时可以配置多个,所以需要所有的都匹配成功了才使用当前路由

c. convertToRoute方法

将路由定义信息转为路由对象

private Route convertToRoute(RouteDefinition routeDefinition) {
   //获取路由下的匹配规则断言,具体实现可以看<路由匹配规则>  之后章节讲解
   AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
   //获取过滤器,包括gateway下的统一配置过滤器和路由下单独配的过滤器,之后章节详细讲解
   List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);

   return Route.async(routeDefinition).asyncPredicate(predicate)
         .replaceFilters(gatewayFilters).build();
}

d. 获取过滤器

配置文件

spring:
  cloud:
    gateway:
      default-filters: // 默认过滤器(全局过滤器)
        - AddRequestHeader=key,value
      routes:
        - id: test
          uri: http://localhost:8081
          predicates:
            - Path=/test/**
            - Query=param, value
          filters: // 路由器下特定过滤器
            - AddRequestHeader=key,value
          order: 1

获取过滤器细节

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

   // 获取全局过滤器,所有路由器共享
   if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
      filters.addAll(loadGatewayFilters(DEFAULT_FILTERS,
            new ArrayList<>(this.gatewayProperties.getDefaultFilters())));
   }
   // 获取路由器下特定过滤器
   if (!routeDefinition.getFilters().isEmpty()) {
      filters.addAll(loadGatewayFilters(routeDefinition.getId(),
            new ArrayList<>(routeDefinition.getFilters())));
   }
   //过滤器排序
   AnnotationAwareOrderComparator.sort(filters);
   return filters;
}


/** 获取指定过滤器的执行逻辑列表 */
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());
                // 校验抛异常,省略。。。
                // 构建过滤器所需参数
                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();
                // 有些过滤器需要路由的ID
                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;
}

10. 总结

至此,加载路由配置的功能已经完成,可以通过RouteDefinitionRouteLocator的getRoutes()方法获取到所有的路由器列表Flux,它可以对应该请求进行校验是否匹配某个路由规则,并且依次执行全局过滤器以及可选过滤器

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值