Spring-Cloud-Gateway源码分析系列 | Spring-Cloud-Gateway之Route初始化加载

推荐 Spring Boot/Cloud 视频:

Spring-Cloud-Gateway路由信息是通过路由定位器RouteLocator加载以及初始化的接下来阅读源码看下Spring-Cloud-Gateway是怎么一步一步的实现了路由的加载初始化。

首选我们还是在Spring-Cloud-Gateway初始化配置中看Spring-Cloud-Gateway初始化是创建了路由定位相关的那些类

------------- GatewayAutoConfiguration类
    /**
     * 创建一个根据RouteDefinition转换的路由定位器
     */
    @Bean
    public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
                                                   List<GatewayFilterFactory> GatewayFilters,
                                                   List<RoutePredicateFactory> predicates,
                                                   RouteDefinitionLocator routeDefinitionLocator) {
        return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, GatewayFilters, properties);
    }

    /**
     * 创建一个缓存路由的路由定位器
     * @param routeLocators
     * @return
     */
    @Bean
    @Primary//意思是在众多相同的bean中,优先使用用@Primary注解的bean.
    //TODO: property to disable composite?
    public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
        
        //1.创建组合路由定位器,根据(容器)已有的路由定位器集合
        //2.创建缓存功能的路由定位器
        return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
    }

从初始化配置类中可以路由定位器的创建流程

1.RouteDefinitionRouteLocator
2.CompositeRouteLocator
3.CachingRouteLocator

  • 其中 RouteDefinitionRouteLocator 是获取路由的主要地方,CompositeRouteLocator,CachingRouteLocator对路由定位器做了附加功能的包装,最终使用的是CachingRouteLocator对外提供服务

下来阅读RouteLocator 接口源码:

/**
 * 路由定位器,服务获取路由信息
 * 1.可以通过 RouteDefinitionRouteLocator 获取 RouteDefinition ,并转换成 Route
 * @author Spencer Gibb
 */
//TODO: rename to Routes?
public interface RouteLocator {

    /**
     * 获取路由
     * @return
     */
    Flux<Route> getRoutes();
}

接口很简单,有且只有一个获取路由的方法,专门用来获取路由。
RouteLocator 类图如下:

graph TD
RouteLocator-->|缓存功能实现|CachingRouteLocator
RouteLocator-->|组合功能实现|CompositeRouteLocator
RouteLocator-->|通过路由定义转换路由实现|RouteDefinitionRouteLocator

接下来我们依次阅读具体实现类

CachingRouteLocator
/**
 * 路由定位器的包装类,实现了路由的本地缓存功能
 * @author Spencer Gibb
 */
public class CachingRouteLocator implements RouteLocator {

    /**
     * 目标路由定位器
     */
    private final RouteLocator delegate;

    /**
     * 路由信息
     * Flux 相当于一个 RxJava Observable,
     * 能够发出 0~N 个数据项,然后(可选地)completing 或 erroring。处理多个数据项作为stream
     */
    private final Flux<Route> routes;

    /**
     * 本地缓存,用于缓存路由定位器获取的路由集合
     *
     */
    private final Map<String, List> cache = new HashMap<>();
    public CachingRouteLocator(RouteLocator delegate) {
        this.delegate = delegate;
        routes = CacheFlux.lookup(cache, "routes", Route.class)
                .onCacheMissResume(() -> this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE));
    }
    @Override
    public Flux<Route> getRoutes() {
        return this.routes;
    }

    /**
     * Clears the routes cache
     * @return routes flux
     */
    public Flux<Route> refresh() {
        this.cache.clear();
        return this.routes;
    }

    @EventListener(RefreshRoutesEvent.class)
    /* for testing */ void handleRefresh() {
        refresh();
    }
}
  • 此类实现了对路由信息的本地缓存,通过Map<String, List> cache 缓存路由到内存中
  • 此类通过@EventListener(RefreshRoutesEvent.class)监听RefreshRoutesEvent事件实现了对缓存的动态刷新。
    备注:动态刷新可以在GatewayControllerEndpoint中通过http请求发布刷新事件
@RestControllerEndpoint(id = "gateway")
public class GatewayControllerEndpoint implements ApplicationEventPublisherAware{
    // 调用url= /gateway/refresh 刷新缓存中的路由信息
        @PostMapping("/refresh")
    public Mono<Void> refresh() {
        this.publisher.publishEvent(new RefreshRoutesEvent(this));
        return Mono.empty();
    }
}
  • CompositeRouteLocator
/**
 *
 *  组合多个 RRouteLocator 的实现,为Route提供统一获取入口
 * @author Spencer Gibb
 */
public class CompositeRouteLocator implements RouteLocator {

    /**
     * 能够发出 0~N 个数据项(RouteLocator),然后(可选地)completing 或 erroring。处理多个数据项作为stream
     */
    private final Flux<RouteLocator> delegates;

    public CompositeRouteLocator(Flux<RouteLocator> delegates) {
        this.delegates = delegates;
    }

    @Override
    public Flux<Route> getRoutes() {
        //this.delegates.flatMap((routeLocator)-> routeLocator.getRoutes());
        return this.delegates.flatMap(RouteLocator::getRoutes);
    }
}

此类将遍历传入的目录路由定位器集合,组合每个路由定位器获取到的路由信息

  • RouteDefinitionRouteLocator
/**
 * 路由定位器
 *  此实现通过路由定义(RouteDefinition)转换路由(Route)
 * {@link RouteLocator} that loads routes from a {@link RouteDefinitionLocator}
 * @author Spencer Gibb
 */
public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {
    
        public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,
                                       List<RoutePredicateFactory> predicates,
                                       List<GatewayFilterFactory> gatewayFilterFactories,
                                       GatewayProperties gatewayProperties) {
        this.routeDefinitionLocator = routeDefinitionLocator;
        initFactories(predicates);
        gatewayFilterFactories.forEach(factory -> this.gatewayFilterFactories.put(factory.name(), factory));
        this.gatewayProperties = gatewayProperties;
    }
    
        @Override
    public Flux<Route> getRoutes() {
        //获取到所有的RouteDefinition
        return this.routeDefinitionLocator.getRouteDefinitions()
                //遍历转换成对应的Route信息
                .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());
            }*/
    }
}
- 此类的核心方法getRoutes通过传入的routeDefinitionLocator获取路由定位,并循环遍历路由定位依次转换成路由返回,
- 代码中可以看到getRoutes通过convertToRoute方法将路由定位转换成路由的
  • RouteDefinitionRouteLocator:RouteDefinition转换
    /**
     * RouteDefinition 转换为对应的Route
     * @param routeDefinition
     * @return
     */
    private Route convertToRoute(RouteDefinition routeDefinition) {
        //获取routeDefinition中的Predicate信息
        Predicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
        //获取routeDefinition中的GatewayFilter信息
        List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);
        //构建路由信息
        return Route.builder(routeDefinition)
                .predicate(predicate)
                .replaceFilters(gatewayFilters)
                .build();
    }
  • convertToRoute方法功能作用
    • 获取routeDefinition中的Predicate信息 (通过combinePredicates方法)
    • 获取routeDefinition中的GatewayFilter信息(通过gatewayFilters方法)
    • 构建路由信息
  • RouteDefinitionRouteLocator:获取routeDefinition中的Predicate信息
    /**
     * 返回组合的谓词
     * @param routeDefinition
     * @return
     */
    private Predicate<ServerWebExchange> combinePredicates(RouteDefinition routeDefinition) {
        //获取RouteDefinition中的PredicateDefinition集合
        List<PredicateDefinition> predicates = routeDefinition.getPredicates();

        Predicate<ServerWebExchange> predicate = lookup(routeDefinition, predicates.get(0));

        for (PredicateDefinition andPredicate : predicates.subList(1, predicates.size())) {
            Predicate<ServerWebExchange> found = lookup(routeDefinition, andPredicate);
             //流程4
            //返回一个组合的谓词,表示该谓词与另一个谓词的短路逻辑AND
            predicate = predicate.and(found);
        }

        return predicate;
    }

    /**
     * 获取一个谓语定义(PredicateDefinition)转换的谓语
     * @param route
     * @param predicate
     * @return
     */
    @SuppressWarnings("unchecked")
    private Predicate<ServerWebExchange> lookup(RouteDefinition route, PredicateDefinition predicate) {
        //流程1
        //流程1==获取谓语创建工厂
        RoutePredicateFactory<Object> factory = this.predicates.get(predicate.getName());
        if (factory == null) {
            throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName());
        }
        //流程2
        //获取参数
        Map<String, String> args = predicate.getArgs();
        if (logger.isDebugEnabled()) {
            logger.debug("RouteDefinition " + route.getId() + " applying "
                    + args + " to " + predicate.getName());
        }

        //组装参数
        Map<String, Object> properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory);
        //构建创建谓语的配置信息
        Object config = factory.newConfig();
        ConfigurationUtils.bind(config, properties,
                factory.shortcutFieldPrefix(), predicate.getName(), validator);
        if (this.publisher != null) {
            this.publisher.publishEvent(new PredicateArgsEvent(this, route.getId(), properties));
        }
        //流程3
        //通过谓语工厂构建谓语
        return factory.apply(config);
    }

PredicateDefinition 谓语定义以及在前面文中阅读过源码了
获取Predicate流程如下

- 根据PredicateDefinition name 获取 RoutePredicateFactory
- 根据PredicateDefinition args 组装 config信息
- 通过RoutePredicateFactory 根据config信息创建Predicate信息
- 多个Predicate 以短路逻辑AND组合
  • RouteDefinitionRouteLocator:获取routeDefinition中的GatewayFilter信息
    private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
        List<GatewayFilter> filters = new ArrayList<>();

        //校验gatewayProperties是否含义默认的过滤器集合
        //TODO: support option to apply defaults after route specific filters?
        if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
            //加载全局配置的默认过滤器集合
            filters.addAll(loadGatewayFilters("defaultFilters",
                    this.gatewayProperties.getDefaultFilters()));
        }

        if (!routeDefinition.getFilters().isEmpty()) {
            //加载路由定义中的过滤器集合
            filters.addAll(loadGatewayFilters(routeDefinition.getId(), routeDefinition.getFilters()));
        }

        //排序
        AnnotationAwareOrderComparator.sort(filters);
        return filters;
    }
        /**
     * 加载过滤器,根据过滤器的定义加载
     * @param id
     * @param filterDefinitions
     * @return
     */
    @SuppressWarnings("unchecked")
    private List<GatewayFilter> loadGatewayFilters(String id, List<FilterDefinition> filterDefinitions) {
        //遍历过滤器定义,将过滤器定义转换成对应的过滤器
        List<GatewayFilter> filters = filterDefinitions.stream()
                .map(definition -> {
                
                   //流程1    //通过过滤器定义名称获取过滤器创建工厂
                    GatewayFilterFactory factory = this.gatewayFilterFactories.get(definition.getName());
                    if (factory == null) {
                        throw new IllegalArgumentException("Unable to find GatewayFilterFactory with name " + definition.getName());
                    }
                    //流程2
                    //获取参数
                    Map<String, String> args = definition.getArgs();
                    if (logger.isDebugEnabled()) {
                        logger.debug("RouteDefinition " + id + " applying filter " + args + " to " + definition.getName());
                    }

                    //根据args组装配置信息
                    Map<String, Object> properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory);
                    //构建过滤器创建配置信息
                    Object configuration = factory.newConfig();
                    ConfigurationUtils.bind(configuration, properties,
                            factory.shortcutFieldPrefix(), definition.getName(), validator);

//流程3
                    //通过过滤器工厂创建GatewayFilter
                    GatewayFilter gatewayFilter = factory.apply(configuration);
                    if (this.publisher != null) {
                        //发布事件
                        this.publisher.publishEvent(new FilterArgsEvent(this, id, properties));
                    }
                    return gatewayFilter;
                })
                .collect(Collectors.toList());

        ArrayList<GatewayFilter> ordered = new ArrayList<>(filters.size());
        //包装过滤器使其所有过滤器继承Ordered属性,可进行排序
        for (int i = 0; i < filters.size(); i++) {
            GatewayFilter gatewayFilter = filters.get(i);
            if (gatewayFilter instanceof Ordered) {
                ordered.add(gatewayFilter);
            }
            else {
                ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
            }
        }

        return ordered;
    }
  • getFilters 方法 同时加载 全局配置 gatewayProperties与routeDefinition配置下的所有过滤器定义filterDefinitions

  • loadGatewayFilters 负责将filterDefinition转化成对应的GatewayFilter
    转化流程如下

    • 根据filterDefinition name 获取 GatewayFilterFactory
    • 根据filterDefinition args 组装 config信息
    • 通过GatewayFilterFactory 根据config信息创建PGatewayFilter信息
      至此,阅读了路由的数据模型,路由定义加载,路由的加载(路由定义转化为路由),Spring-Cloud-Gateway路由的创建加载流程已经清晰明了的展现出来,在使用中也可以快速的对其扩展,以及根据实际需求进行个性化的定制操作。

专家推荐

“随着微服务架构的发展,Spring Cloud 使用得越来越广泛。驰狼课堂 Spring Boot 快速入门,Spring Boot 与Spring Cloud 整合,docker+k8s,大型电商商城等多套免费实战教程可以帮您真正做到快速上手,将技术点切实运用到微服务项目中。”

关注公众号,每天精彩内容,第一时间送达!
《Spring-Cloud-Gateway源码分析系列 | Spring-Cloud-Gateway之Route初始化加载》

    原文作者:Spring Boot
    原文地址: https://blog.csdn.net/qq_43253123/article/details/83311710
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞