国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

spring cloud gateway 源碼解析(2)動態(tài)路由

劉永祥 / 857人閱讀

摘要:先看看是怎么獲取我們配置的路由在啟動時,幫我們注冊了一系列這里注入所有,我們在配置文件里配置的路由就是通過子類來完成的,可以參考實現(xiàn)自己的的存儲,會在后面轉換成接著看類里的獲取路由定義方法即輪訓所有的市現(xiàn)率調用,這樣就把所有整合到一起了接著

先看看gateway是怎么獲取我們配置的路由:
在gateway啟動時,GatewayAutoConfiguration幫我們注冊了一系列beans

    @Bean
    @Primary
    public RouteDefinitionLocator routeDefinitionLocator(List routeDefinitionLocators) {
        //這里注入所有RouteDefinitionLocator,我們在配置文件里配置的路由就是通過子類PropertiesRouteDefinitionLocator來完成的,
        //可以參考InMemoryRouteDefinitionRepository實現(xiàn)自己的RouteDefinition的存儲,RouteDefinition會在后面轉換成Route
        return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));
    }

    //接著看CompositeRouteDefinitionLocator類里的獲取路由定義方法:
    public Flux getRouteDefinitions() {
        //即輪訓所有RouteDefinitionLocator的市現(xiàn)率調用getRouteDefinitions,這樣就把所有RouteDefinition整合到一起了
        return this.delegates.flatMap(RouteDefinitionLocator::getRouteDefinitions);
    }
    
    //接著看GatewayAutoConfiguration,剛才的bean RouteDefinitionLocator 作為參數(shù)注入到routeDefinitionLocator
    @Bean
    public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
                                                   List GatewayFilters,
                                                   List predicates,
                                                   RouteDefinitionLocator routeDefinitionLocator) {
        return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, GatewayFilters, properties);
    }

 //看RouteDefinitionRouteLocator類:
public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {
    ······

    public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,
                                       List predicates,
                                       List gatewayFilterFactories,
                                       GatewayProperties gatewayProperties) {
        this.routeDefinitionLocator = routeDefinitionLocator;
        //RoutePredicateFactory放到map里,去掉RoutePredicateFactory后綴,所以配置時就可以寫成
        //predicates:
        //    - Path=/abc/*
        //的形式而不用寫完整的PathRoutePredicateFactory    
        initFactories(predicates);
        //把bean名字里的GatewayFilterFactory去掉,所以我們配置文件里配置時也要把GatewayFilterFactory去掉,不然會找不到
        gatewayFilterFactories.forEach(factory -> this.gatewayFilterFactories.put(factory.name(), factory));
        this.gatewayProperties = gatewayProperties;
    }
    ······ 

    @Override
    public Flux getRoutes() {
        return this.routeDefinitionLocator.getRouteDefinitions()
                .map(this::convertToRoute)//獲取所有的RouteDefinition后轉為Route
                //TODO: error handling
                .map(route -> {
                    if (logger.isDebugEnabled()) {
                        logger.debug("RouteDefinition matched: " + route.getId());
                    }
                    return route;
                });
 
    }

    private Route convertToRoute(RouteDefinition routeDefinition) {
        //獲取所有匹配規(guī)則,所有的規(guī)則都滿足才走對應Route
        AsyncPredicate predicate = combinePredicates(routeDefinition);
        //獲取全部過濾器
        List gatewayFilters = getFilters(routeDefinition);

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

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

        //把全局過濾器添加到當前路由,loadGatewayFilters調用GatewayFilterFactory里的apply配置類里面的靜態(tài)Config類,并且把沒實現(xiàn)Ordered接口的類用OrderedGatewayFilter代理一下,方便下面排序
        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;
    }

    private AsyncPredicate combinePredicates(RouteDefinition routeDefinition) { 
        //獲取路由的判斷條件,比如我們大多是根據(jù)url判斷,用的是PathRoutePredicateFactory
        List predicates = routeDefinition.getPredicates();
        AsyncPredicate predicate = lookup(routeDefinition, predicates.get(0));

        for (PredicateDefinition andPredicate : predicates.subList(1, predicates.size())) {
            AsyncPredicate found = lookup(routeDefinition, andPredicate);
            predicate = predicate.and(found);//如果有多個匹配規(guī)則就要滿足所有的才可以
        }
        //返回的結果會在RoutePredicateHandlerMapping類里使用apply方法調用 
        return predicate;
    }

    @SuppressWarnings("unchecked")
    private AsyncPredicate lookup(RouteDefinition route, PredicateDefinition predicate) {
        RoutePredicateFactory factory = this.predicates.get(predicate.getName());
        if (factory == null) {
            throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName());
        }
        Map args = predicate.getArgs();
        if (logger.isDebugEnabled()) {
            logger.debug("RouteDefinition " + route.getId() + " applying "
                    + args + " to " + predicate.getName());
        }

        Map properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory);
        //獲取RoutePredicateFactory實現(xiàn)類里的靜態(tài)Config
        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));
        }
        //這里實際調用的是ServerWebExchangeUtils類里的toAsyncPredicate方法,并配置的config
        return factory.applyAsync(config);
    }
}


  // 把剛才的bean RouteDefinitionRouteLocator作為參數(shù)注入到routeLocators
    @Bean
    @Primary 
    public RouteLocator cachedCompositeRouteLocator(List routeLocators) {
        //這里CompositeRouteLocator與上面的CompositeRouteDefinitionLocator類似,也是遍歷routeLocators調用getRoutes方法整合所有Route
        return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
    }
    //接著看CachingRouteLocator類
    public class CachingRouteLocator implements RouteLocator {

    private final RouteLocator delegate;
    private final Flux routes;
    private final Map cache = new HashMap<>();

    public CachingRouteLocator(RouteLocator delegate) {
        this.delegate = delegate;
        routes = CacheFlux.lookup(cache, "routes", Route.class)
                 //每次請求都會獲取routes,每當拿緩存又找不到的時候(即我們修改了路由),就會調用上面CompositeRouteLocator的getRoutes方法重新獲取路由
                .onCacheMissResume(() -> this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE));
    }

    ·······

    public Flux refresh() {
        this.cache.clear();//清掉路由緩存
        return this.routes;
    }

    @EventListener(RefreshRoutesEvent.class)
    /* for testing */ void handleRefresh() {//更新路由用的是spring事件機制
        refresh();
    }
}

 //接著看GatewayAutoConfiguration,剛才的bean CachingRouteLocator作為參數(shù)注入到RouteLocator
    @Bean
    public RoutePredicateHandlerMapping routePredicateHandlerMapping(
            FilteringWebHandler webHandler, RouteLocator routeLocator,
            GlobalCorsProperties globalCorsProperties, Environment environment) {
        return new RoutePredicateHandlerMapping(webHandler, routeLocator,
                globalCorsProperties, environment);
    }

//接著看RoutePredicateHandlerMapping

public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {

    private final FilteringWebHandler webHandler;
    private final RouteLocator routeLocator;
    private final Integer managmentPort;

    public RoutePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) {
        this.webHandler = webHandler;
        this.routeLocator = routeLocator;

        if (environment.containsProperty("management.server.port")) {
            managmentPort = new Integer(environment.getProperty("management.server.port"));
        } else {
            managmentPort = null;
        }
        //這里設置了順序,使得網關路由的匹配在requestmapping和handlefunction之后
        setOrder(1);        
        setCorsConfigurations(globalCorsProperties.getCorsConfigurations());
    }

    @Override//方法在源碼解析(1)中說的AbstractHandlerMapping類中獲取Handler時調用
    protected Mono getHandlerInternal(ServerWebExchange exchange) {
        // don"t handle requests on the management port if set
        if (managmentPort != null && exchange.getRequest().getURI().getPort() == managmentPort.intValue()) {
            //不處理特定配置端口的路由,返回empty即不用網關路由的handler,AbstractHandlerMapping繼續(xù)找下一個
            return Mono.empty();
        }
        exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName());

        return lookupRoute(exchange)//查找路由
                // .log("route-predicate-handler-mapping", Level.FINER) //name this
                .flatMap((Function>) r -> {
                    exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
                    if (logger.isDebugEnabled()) {
                        logger.debug("Mapping [" + getExchangeDesc(exchange) + "] to " + r);
                    }
                    //把Route放到exchange的屬性了,后面有幾個過濾器用到了
                    exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
                    //返回構造該類時傳入的FilteringWebHandler,之后的流程就會跟上一篇里說的走下去了
                    return Mono.just(webHandler);
                }).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
                   //找不到匹配的路由的話進到這里,之后基本就是404了
                    exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
                    if (logger.isTraceEnabled()) {
                        logger.trace("No RouteDefinition found for [" + getExchangeDesc(exchange) + "]");
                    }
                })));
    }

     ······

    protected Mono lookupRoute(ServerWebExchange exchange) {
       //調用上面提到的CachingRouteLocator的getRoutes方法獲取所有Routes
        return this.routeLocator
                .getRoutes()
                //individually filter routes so that filterWhen error delaying is not a problem
                .concatMap(route -> Mono
                        .just(route)
                        .filterWhen(r -> {//過濾出符合的路由
                            // add the current route we are testing
                            exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
                            //調用上面說的ServerWebExchangeUtils類里的toAsyncPredicate方法返回的lambda 
                            //里面會調用Predicate的實現(xiàn)類(都是lambda)的test方法返回true或者false
                            return r.getPredicate().apply(exchange);
                        })
                        //instead of immediately stopping main flux due to error, log and swallow it
                        .doOnError(e -> logger.error("Error applying predicate for route: "+route.getId(), e))
                        .onErrorResume(e -> Mono.empty())
                )
                // .defaultIfEmpty() put a static Route not found
                // or .switchIfEmpty()
                // .switchIfEmpty(Mono.empty().log("noroute"))
                .next()//過濾出一個后就停止了
                //TODO: error handling
                .map(route -> {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Route matched: " + route.getId());
                    }
                    validateRoute(route, exchange);
                    return route;
                });
 
    }

   ·····
}

說了那么多終于把網關匹配路由的流程說完了,如果上面都看明白的話,動態(tài)路由就好辦了
網關已經把InMemoryRouteDefinitionRepository注冊成bean(也可以參考這個類自己實現(xiàn)RouteDefinitionRepository接口),我們把它當作個service注入到controller,
前端把RouteDefinition用json的格式post過來,我們調用InMemoryRouteDefinitionRepository的save或者delete方法,再用spring的事件觸發(fā)RefreshRoutesEvent事件來刷新路由就行了,等下次請求的時候就可以拿到新的路由配置了
順序是:
1.RoutePredicateHandlerMapping 的lookupRoute方法,由于路由刷新事件把路由緩存清了,所以重新獲取
2.CompositeRouteLocator 的getRoutes方法遍歷所有RouteLocator實現(xiàn)類的getRoutes方法
3.RouteDefinitionRouteLocator 的getRoutes方法里重新獲取了所有的路由定義,也就把我們剛剛用事件添加的路由也獲取了

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/77399.html

相關文章

  • Spring Cloud Gateway 擴展支持動態(tài)限流

    摘要:以流量為切入點,從流量控制熔斷降級系統(tǒng)負載保護等多個維度保護服務的穩(wěn)定性分布式系統(tǒng)的流量防衛(wèi)兵。歡迎關注我們獲得更多的好玩實踐 之前分享過 一篇 《Spring Cloud Gateway 原生的接口限流該怎么玩》, 核心是依賴Spring Cloud Gateway 默認提供的限流過濾器來實現(xiàn) 原生RequestRateLimiter 的不足 配置方式 spring: clou...

    妤鋒シ 評論0 收藏0
  • Spring Cloud Gateway 擴展支持動態(tài)限流

    摘要:以流量為切入點,從流量控制熔斷降級系統(tǒng)負載保護等多個維度保護服務的穩(wěn)定性分布式系統(tǒng)的流量防衛(wèi)兵。歡迎關注我們獲得更多的好玩實踐 之前分享過 一篇 《Spring Cloud Gateway 原生的接口限流該怎么玩》, 核心是依賴Spring Cloud Gateway 默認提供的限流過濾器來實現(xiàn) 原生RequestRateLimiter 的不足 配置方式 spring: clou...

    beanlam 評論0 收藏0
  • spring cloud gateway 源碼解析(1)整體流程

    摘要:公司要做自己的網關,于是先把的過了一遍,然后把源碼在看了一遍,這樣公司的需求就搞定了。包括動態(tài)路由,多緯度限流,記錄請求參數(shù)及返回參數(shù)也可修改。至此,流程就走完了。 公司要做自己的網關,于是先把github的issue過了一遍,然后把gateway源碼在看了一遍,這樣公司的需求就搞定了。包括動態(tài)路由,多緯度限流,記錄請求參數(shù)及返回參數(shù)(也可修改)。先從請求進入網關說起吧: 請求先進...

    miqt 評論0 收藏0
  • 如何使用SpringCloud進行灰度發(fā)布

    摘要:灰度發(fā)布是指在黑與白之間,能夠平滑過渡的一種發(fā)布方式。如何使用進行灰度發(fā)布呢將分一下四步第一,設置網關權重路由設置中提供了去實現(xiàn)根據(jù)分組設置權重進行路由,因此使用起來相對比較簡單,有興趣的可以閱讀源碼。 灰度發(fā)布是指在黑與白之間,能夠平滑過渡的一種發(fā)布方式。在其上可以進行A/B testing,即讓一部分用戶繼續(xù)用產品特性A,一部分用戶開始用產品特性B,如果用戶對B沒有什么反對意見,那...

    Jackwoo 評論0 收藏0

發(fā)表評論

0條評論

劉永祥

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<