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

扫码关注公众号:Java 技术驿站

发送:vip
将链接复制到本浏览器,永久解锁本站全部文章

【公众号:Java 技术驿站】 【加作者微信交流技术,拉技术群】

推荐 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,大型电商商城等多套免费实战教程可以帮您真正做到快速上手,将技术点切实运用到微服务项目中。”

关注公众号,每天精彩内容,第一时间送达!
20191017100366\_1.png


来源:[]()

赞(0) 打赏
版权归原创作者所有,任何形式的转载请联系博主:daming_90:Java 技术驿站 » Spring-Cloud-Gateway源码分析系列 | Spring-Cloud-Gateway之Route初始化加载

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏