1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Spring Cloud Gateway 源碼剖析之Route數(shù)據(jù)模型

        共 64493字,需瀏覽 129分鐘

         ·

        2021-04-02 12:39

        點擊上方老周聊架構(gòu)關(guān)注我


        一、前言

        我們上一篇講了:Spring Cloud Gateway 源碼剖析之配置初始化,通過自動加載初始化六個配置實例,Spring Cloud Gateway 就完成自身的加載和初始化工作。我們知道 Gateway 的核心是路由加過濾,既然網(wǎng)關(guān)相關(guān)初始化工作做好了,那得開始路由相關(guān)的工作了。

        接下來我們就來分析下平時在 properties 或者 yml 中配置的有關(guān) Gateway 的配置是如何構(gòu)建成 Route 的。

        二、Route 構(gòu)建方式

        一般構(gòu)建分為兩種:外部化配置和編程方式

        1、外部化配置

        spring:  cloud:    gateway:      routes:       - id: after_route // ①        uri: https://example.org // ②        predicates:         - Cookie=mycookie,mycookievalue // ③        filters:         - AddRequestHeader=X-Request-Foo, Bar // ④
        • ① 配置了一個 Route id 為 after_route

        • ② 客戶端請求轉(zhuǎn)發(fā)的目的地:https://example.org

        • ③ 在 request 中,當存在名字 mycookie 的 cookie 的值匹配 mycookievalue 則算成功

        • ④ 定義了一個 Filter,匹配成功后,會在請求頭上添加 X-Request-Foo:Bar

        2、編程方式

        用上面的例子,轉(zhuǎn)換成編程方式

        @Bean
        public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
           builder.routes()
                .route(r -> r.cookie("mycookie""mycookievalue")
                    .filters(f -> f.addRequestHeader("X-Request-Foo""Bar"))
                    .uri("https://example.org")
                )
                .build();
        }

        簡單介紹了構(gòu)建 Route 的兩種方式,下面分析 Route 是如何把外部化配置與編碼配置之間進行轉(zhuǎn)換。

        三、Route 構(gòu)建原理

        1、外部化配置

        外部化配置是通過 GatewayProperties 進行構(gòu)建的

        /**
         * 網(wǎng)關(guān)配置信息加載
         * 從appliccation.yml中解析前綴為spring.cloud.gateway的配置
         */

        @ConfigurationProperties("spring.cloud.gateway")
        @Validated
        public class GatewayProperties {
            private final Log logger = LogFactory.getLog(this.getClass());

            /**
             * 路由定義列表
             * 加載配置key=spring.cloud.gateway.routes 列表
             * List of Routes
             */

            @NotNull
            @Valid
            private List<RouteDefinition> routes = new ArrayList();

            /**
             * 默認的過濾器定義列表
             * 加載配置 key = spring.cloud.gateway.default-filters 列表
             * List of filter definitions that are applied to every route.
             */

            private List<FilterDefinition> defaultFilters = new ArrayList();

            /**
             * 網(wǎng)媒體類型列表
             * 加載配置 key = spring.cloud.gateway.streamingMediaTypes 列表
             * 默認包含{text/event-stream,application/stream+json}
             */

            private List<MediaType> streamingMediaTypes;

            public GatewayProperties() {
                this.streamingMediaTypes = Arrays.asList(MediaType.TEXT_EVENT_STREAM, MediaType.APPLICATION_STREAM_JSON);
            }

            ...
        }

        1.1 RouteDefinition

        用來對 Route 進行定義。也就是,通過 GatewayProperties 會與外部化配置進行綁定,把外部化配置比如 properties 或者 yml 綁定到 GatewayProperties 中。

        /**
         * 路由定義實體信息,包含路由的定義信息
         */

        @Validated
        public class RouteDefinition {
            /**
             * 路由ID 編號,唯一
             */

            @NotEmpty
            private String id = UUID.randomUUID().toString();

            /**
             * 謂語定義數(shù)組
             * predicates 屬性,謂語定義數(shù)組
             * 請求通過   判斷是否匹配。在 Route 里,PredicateDefinition 轉(zhuǎn)換成 Predicate
             */

            @NotEmpty
            @Valid
            private List<PredicateDefinition> predicates = new ArrayList();

            /**
             * 過濾器定義數(shù)組
             * filters 屬性,過濾器定義數(shù)組。
             * 在 Route 里,F(xiàn)ilterDefinition 轉(zhuǎn)換成 GatewayFilter
             */

            @Valid
            private List<FilterDefinition> filters = new ArrayList();

            /**
             * 路由指向的URI
             */

            @NotNull
            private URI uri;

            /**
             * 順序
             */

            private int order = 0;

            ...
        }

        1.2 PredicateDefinition

        /**
         * 謂語定義,在 Route 里,PredicateDefinition 將轉(zhuǎn)換成 Predicate
         */

        @Validated
        public class PredicateDefinition {
            /**
             * 謂語定義名字
             * 通過 name 對應到 org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory 的實現(xiàn)類。
             * 例如: name=Query 對應到 QueryRoutePredicateFactory
             */

            @NotNull
            private String name;

            /**
             * 參數(shù)數(shù)組
             * 例如,name=Host / args={"_genkey_0" : "iocoder.cn"} ,匹配請求的 hostname 為 iocoder.cn
             */

            private Map<String, String> args = new LinkedHashMap();

            ...
        }

        1.3 FilterDefinition

        /**
         * 過濾器定義,在 Route 里,F(xiàn)ilterDefinition將轉(zhuǎn)換成 GatewayFilter
         */

        @Validated
        public class FilterDefinition {
            /**
             * 過濾器定義名字
             * 通過 name 對應到 org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory 的實現(xiàn)類。
             * 例如,name=AddRequestParameter 對應到 AddRequestParameterGatewayFilterFactory
             */

            @NotNull
            private String name;

            /**
             * 參數(shù)數(shù)組
             * 例如 name=AddRequestParameter / args={"_genkey_0": "foo", "_genkey_1": "bar"} ,添加請求參數(shù) foo 為 bar
             */

            private Map<String, String> args = new LinkedHashMap();

            ...
        }

        1.4 RouteDefinitionLocator

        在上一篇文章 Spring Cloud Gateway 源碼剖析之配置初始化 中的 GatewayAutoConfiguration 核心配置類中,有提到此類。

        @Bean
        @Primary
        public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {
            return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));
        }

        Gateway 提供多種方式來獲取外部的配置,而 RouteDefinitionLocator 就是父接口,下面有多種實現(xiàn)。

        在這里插入圖片描述

        1.4.1 PropertiesRouteDefinitionLocator

        從配置文件(YML、Properties 等) 讀取路由配置。代碼如下:

        public class PropertiesRouteDefinitionLocator implements RouteDefinitionLocator {
            private final GatewayProperties properties;

            public PropertiesRouteDefinitionLocator(GatewayProperties properties) {
                this.properties = properties;
            }

            public Flux<RouteDefinition> getRouteDefinitions() {
                // 從 GatewayProperties 獲取路由配置數(shù)組。
                return Flux.fromIterable(this.properties.getRoutes());
            }
        }

        1.4.2 InMemoryRouteDefinitionRepository

        從存儲器(內(nèi)存、Redis、MySQL 等)讀取、保存、刪除路由配置。InMemoryRouteDefinitionRepository 是基于內(nèi)存的。

        public class InMemoryRouteDefinitionRepository implements RouteDefinitionRepository {
            /**
                * 路由配置映射 通過此來保存route
             * key :路由編號 {@link RouteDefinition#id}
             */

            private final Map<String, RouteDefinition> routes = Collections.synchronizedMap(new LinkedHashMap());

            public InMemoryRouteDefinitionRepository() {
            }

            public Mono<Void> save(Mono<RouteDefinition> route) {
                return route.flatMap((r) -> {
                    this.routes.put(r.getId(), r);
                    return Mono.empty();
                });
            }

            public Mono<Void> delete(Mono<String> routeId) {
                return routeId.flatMap((id) -> {
                    if (this.routes.containsKey(id)) {
                        this.routes.remove(id);
                        return Mono.empty();
                    } else {
                        return Mono.defer(() -> {
                            return Mono.error(new NotFoundException("RouteDefinition not found: " + routeId));
                        });
                    }
                });
            }

            public Flux<RouteDefinition> getRouteDefinitions() {
                return Flux.fromIterable(this.routes.values());
            }
        }

        基于內(nèi)存,通過 Map routes 來保存 Route,缺點是如果重啟,那么 route 會丟失??梢詫崿F(xiàn) RouteDefinitionRepository 接口自定義比如通過 Redis、Mysql 等來保存 Route。

        1.4.3 DiscoveryClientRouteDefinitionLocator

        獲取在注冊中心的服務列表,生成對應的 RouteDefinition 數(shù)組。

        public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLocator {
            private static final Log log = LogFactory.getLog(DiscoveryClientRouteDefinitionLocator.class);
            private final DiscoveryClient discoveryClient;
            private final DiscoveryLocatorProperties properties;
            private final String routeIdPrefix;
            private final SimpleEvaluationContext evalCtxt;

            public DiscoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {
                this.discoveryClient = discoveryClient;
                this.properties = properties;
                if (StringUtils.hasText(properties.getRouteIdPrefix())) {
                    this.routeIdPrefix = properties.getRouteIdPrefix();
                } else {
                    this.routeIdPrefix = this.discoveryClient.getClass().getSimpleName() + "_";
                }

                this.evalCtxt = SimpleEvaluationContext.forReadOnlyDataBinding().withInstanceMethods().build();
            }

            public Flux<RouteDefinition> getRouteDefinitions() {
                SpelExpressionParser parser = new SpelExpressionParser();
                Expression includeExpr = parser.parseExpression(this.properties.getIncludeExpression());
                Expression urlExpr = parser.parseExpression(this.properties.getUrlExpression());
                Predicate includePredicate;
                if (this.properties.getIncludeExpression() != null && !"true".equalsIgnoreCase(this.properties.getIncludeExpression())) {
                    includePredicate = (instance) -> {
                        Boolean include = (Boolean)includeExpr.getValue(this.evalCtxt, instance, Boolean.class);
                        return include == null ? false : include;
                    };
                } else {
                    includePredicate = (instance) -> {
                        return true;
                    };
                }
                // 獲取discoveryClient,然后發(fā)起請求
                Flux var10000 = Flux.fromIterable(this.discoveryClient.getServices());
                DiscoveryClient var10001 = this.discoveryClient;
                var10001.getClass();
                return var10000.map(var10001::getInstances).filter((instances) -> {
                    return !instances.isEmpty();
                }).map((instances) -> {
                    return (ServiceInstance)instances.get(0);
                }).filter(includePredicate).map((instance) -> {
                    String serviceId = instance.getServiceId();
                    RouteDefinition routeDefinition = new RouteDefinition();
                    // 設置 ID
                    routeDefinition.setId(this.routeIdPrefix + serviceId);
                    // 設置 uri
                    String uri = (String)urlExpr.getValue(this.evalCtxt, instance, String.class);
                    routeDefinition.setUri(URI.create(uri));
                    ServiceInstance instanceForEval = new DiscoveryClientRouteDefinitionLocator.DelegatingServiceInstance(instance, this.properties);
                    Iterator var8 = this.properties.getPredicates().iterator();

                    Iterator var11;
                    Entry entry;
                    String value;

                    while(var8.hasNext()) {
                        PredicateDefinition originalx = (PredicateDefinition)var8.next();
                        // 添加 path 斷言
                        PredicateDefinition predicate = new PredicateDefinition();
                        predicate.setName(originalx.getName());
                        var11 = originalx.getArgs().entrySet().iterator();

                        while(var11.hasNext()) {
                            entry = (Entry)var11.next();
                            value = this.getValueFromExpr(this.evalCtxt, parser, instanceForEval, entry);
                            predicate.addArg((String)entry.getKey(), value);
                        }

                        routeDefinition.getPredicates().add(predicate);
                    }

                    var8 = this.properties.getFilters().iterator();

                    while(var8.hasNext()) {
                        FilterDefinition original = (FilterDefinition)var8.next();
                        // 添加path 重寫過濾器
                        FilterDefinition filter = new FilterDefinition();
                        filter.setName(original.getName());
                        var11 = original.getArgs().entrySet().iterator();

                        while(var11.hasNext()) {
                            entry = (Entry)var11.next();
                            value = this.getValueFromExpr(this.evalCtxt, parser, instanceForEval, entry);
                            filter.addArg((String)entry.getKey(), value);
                        }

                        routeDefinition.getFilters().add(filter);
                    }

                    return routeDefinition;
                });
            }

            ...
        }

        可以在官方的 GatewaySampleApplication 添加 Eureka 注冊中心自行調(diào)試:

        // 開啟Eureka
        @EnableDiscoveryClient 
        public class GatewaySampleApplication {
            // ... 省略其他代碼

            @Bean
            public RouteDefinitionLocator discoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient) {
                return new DiscoveryClientRouteDefinitionLocator(discoveryClient);
            }
        }

        當然要自己加入 Eureka 依賴以及配置文件

        1.4.4 CachingRouteDefinitionLocator

        public class CachingRouteDefinitionLocator implements RouteDefinitionLocatorApplicationListener<RefreshRoutesEvent{
            private final RouteDefinitionLocator delegate;
            private final Flux<RouteDefinition> routeDefinitions;
            // 收集Route
            private final Map<String, List> cache = new HashMap();

            public CachingRouteDefinitionLocator(RouteDefinitionLocator delegate) {
                this.delegate = delegate;
                FluxCacheBuilderMapMiss var10001 = CacheFlux.lookup(this.cache, "routeDefs", RouteDefinition.class);
                RouteDefinitionLocator var10002 = this.delegate;
                var10002.getClass();
                this.routeDefinitions = var10001.onCacheMissResume(var10002::getRouteDefinitions);
            }

            public Flux<RouteDefinition> getRouteDefinitions() {
                return this.routeDefinitions;
            }

            public Flux<RouteDefinition> refresh() {
                this.cache.clear();
                return this.routeDefinitions;
            }

            public void onApplicationEvent(RefreshRoutesEvent event) {
                this.refresh();
            }

            /** @deprecated */
            @Deprecated
            void handleRefresh() {
                this.refresh();
            }
        }

        1.4.5 CompositeRouteDefinitionLocator

        組合多種 RouteDefinitionLocator 的實現(xiàn),為 RouteDefinitionRouteLocator 提供統(tǒng)一入口。

        public class CompositeRouteDefinitionLocator implements RouteDefinitionLocator {
            // RouteDefinitionLocator 數(shù)組
            private final Flux<RouteDefinitionLocator> delegates;

            public CompositeRouteDefinitionLocator(Flux<RouteDefinitionLocator> delegates) {
                this.delegates = delegates;
            }

            // 將組合的 delegates 的路由定義全部返回。
            public Flux<RouteDefinition> getRouteDefinitions() {
                return this.delegates.flatMap(RouteDefinitionLocator::getRouteDefinitions);
            }
        }

        到此為止外部化的配置的多種方式全部解析完畢。接下來我們來看下編程方式。

        2、編程方式

        // org.springframework.cloud.gateway.sample.GatewaySampleApplication
        @SpringBootConfiguration
        @EnableAutoConfiguration
        @Import(AdditionalRoutesImportSelector.class)
        public class GatewaySampleApplication {

            public static final String HELLO_FROM_FAKE_ACTUATOR_METRICS_GATEWAY_REQUESTS = "hello from fake /actuator/metrics/gateway.requests";

            @Value("${test.uri:http://httpbin.org:80}")
            String uri;

            public static void main(String[] args) {
                SpringApplication.run(GatewaySampleApplication.class, args);
            }

            @Bean
            public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
                // @formatter:off
                // String uri = "http://httpbin.org:80";
                // String uri = "http://localhost:9080";
                // ① RouteLocatorBuilder 可以構(gòu)建多個路由信息。
                return builder.routes()
                        // ② 指定了 Predicates,這里包含兩個:
                        // 請求頭Host需要匹配**.abc.org,通過 HostRoutePredicateFactory 產(chǎn)生。
                        // 請求路徑需要匹配/anything/png,通過 PathRoutePredicateFactory 產(chǎn)生。
                        .route(r -> r.host("**.abc.org").and().path("/anything/png")
                            // ③ 指定了一個 Filter,下游服務響應后添加響應頭X-TestHeader:foobar,
                            // 通過 AddResponseHeaderGatewayFilterFactory 產(chǎn)生。
                            .filters(f ->
                                    f.prefixPath("/httpbin")
                                            .addResponseHeader("X-TestHeader""foobar"))
                             // ④ 指定路由轉(zhuǎn)發(fā)的目的地 uri。
                            .uri(uri)
                        )
                        .route("read_body_pred", r -> r.host("*.readbody.org")
                                .and().readBody(String.class,
                                                s -> s.trim().equalsIgnoreCase("hi"))
                            .filters(f -> f.prefixPath("/httpbin")
                                    .addResponseHeader("X-TestHeader""read_body_pred")
                            ).uri(uri)
                        )
                        .route("rewrite_request_obj", r -> r.host("*.rewriterequestobj.org")
                            .filters(f -> f.prefixPath("/httpbin")
                                    .addResponseHeader("X-TestHeader""rewrite_request")
                                    .modifyRequestBody(String.class, Hello.class, MediaType.APPLICATION_JSON_VALUE,
                                            (exchange, s) -> {
                                                return Mono.just(new Hello(s.toUpperCase()));
                                            })
                            ).uri(uri)
                        )
                        .route("rewrite_request_upper", r -> r.host("*.rewriterequestupper.org")
                            .filters(f -> f.prefixPath("/httpbin")
                                    .addResponseHeader("X-TestHeader""rewrite_request_upper")
                                    .modifyRequestBody(String.class, String.class,
                                            (exchange, s) -> {
                                                return Mono.just(s.toUpperCase() + s.toUpperCase());
                                            })
                            ).uri(uri)
                        )
                        .route("rewrite_response_upper", r -> r.host("*.rewriteresponseupper.org")
                            .filters(f -> f.prefixPath("/httpbin")
                                    .addResponseHeader("X-TestHeader""rewrite_response_upper")
                                    .modifyResponseBody(String.class, String.class,
                                            (exchange, s) -> {
                                                return Mono.just(s.toUpperCase());
                                            })
                            ).uri(uri)
                        )
                        .route("rewrite_empty_response", r -> r.host("*.rewriteemptyresponse.org")
                            .filters(f -> f.prefixPath("/httpbin")
                                    .addResponseHeader("X-TestHeader""rewrite_empty_response")
                                    .modifyResponseBody(String.class, String.class,
                                            (exchange, s) -> {
                                                if (s == null) {
                                                    return Mono.just("emptybody");
                                                }
                                                return Mono.just(s.toUpperCase());
                                            })

                            ).uri(uri)
                        )
                        .route("rewrite_response_fail_supplier", r -> r.host("*.rewriteresponsewithfailsupplier.org")
                            .filters(f -> f.prefixPath("/httpbin")
                                    .addResponseHeader("X-TestHeader""rewrite_response_fail_supplier")
                                    .modifyResponseBody(String.class, String.class,
                                            (exchange, s) -> {
                                                if (s == null) {
                                                    return Mono.error(new IllegalArgumentException("this should not happen"));
                                                }
                                                return Mono.just(s.toUpperCase());
                                            })
                            ).uri(uri)
                        )
                        .route("rewrite_response_obj", r -> r.host("*.rewriteresponseobj.org")
                            .filters(f -> f.prefixPath("/httpbin")
                                    .addResponseHeader("X-TestHeader""rewrite_response_obj")
                                    .modifyResponseBody(Map.class, String.class, MediaType.TEXT_PLAIN_VALUE,
                                            (exchange, map) -> {
                                                Object data = map.get("data");
                                                return Mono.just(data.toString());
                                            })
                                    .setResponseHeader("Content-Type", MediaType.TEXT_PLAIN_VALUE)
                            ).uri(uri)
                        )
                        .route(r -> r.path("/image/webp")
                            .filters(f ->
                                    f.prefixPath("/httpbin")
                                            .addResponseHeader("X-AnotherHeader""baz"))
                            .uri(uri)
                        )
                        .route(r -> r.order(-1)
                            .host("**.throttle.org").and().path("/get")
                            .filters(f -> f.prefixPath("/httpbin")
                                            .filter(new ThrottleGatewayFilter()
                                            .setCapacity(1)
                                            .setRefillTokens(1)
                                            .setRefillPeriod(10)
                                            .setRefillUnit(TimeUnit.SECONDS)))
                            .uri(uri)
                        )
                        // ⑤ 創(chuàng)建RouteLocator實例
                        .build();
                // @formatter:on
            }

            @Bean
            public RouterFunction<ServerResponse> testFunRouterFunction() {
                RouterFunction<ServerResponse> route = RouterFunctions.route(RequestPredicates.path("/testfun"),
                        request -> ServerResponse.ok().body(BodyInserters.fromValue("hello")));
                return route;
            }

            @Bean
            public RouterFunction<ServerResponse> testWhenMetricPathIsNotMeet() {
                RouterFunction<ServerResponse> route = RouterFunctions
                        .route(RequestPredicates.path("/actuator/metrics/gateway.requests"), request -> ServerResponse.ok()
                                .body(BodyInserters.fromValue(HELLO_FROM_FAKE_ACTUATOR_METRICS_GATEWAY_REQUESTS)));
                return route;
            }

            static class Hello {
                String message;

                Hello() {
                }

                Hello(String message) {
                    this.message = message;
                }

                public String getMessage() {
                    return message;
                }

                public void setMessage(String message) {
                    this.message = message;
                }
            }

        }


        • ① 使用 Builder 模式構(gòu)建 Route

        • ② 創(chuàng)建 Predicates

        • ③ 創(chuàng)建 Filter

        • ④ 需要轉(zhuǎn)發(fā)的目的地 uri

        • ⑤ 創(chuàng)建 RouteLocator 實例

        // org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder.Builder#build
        public RouteLocator build() {
            return () -> {
                return Flux.fromIterable(this.routes).map((routeBuilder) -> {
                    return routeBuilder.build(); // ①
                });
            };
        }

        // RouteLocator 是 Route 集合
        public interface RouteLocator {
            Flux<Route> getRoutes();
        }

        上面 build 方法返回的是 RouteLocator 對象,它是 Route 的集合所以上面 ① 中的 build 方法就是對應的 Route。

        public Route build() {
            Assert.notNull(this.id, "id can not be null");
            Assert.notNull(this.uri, "uri can not be null");
            AsyncPredicate<ServerWebExchange> predicate = this.getPredicate();
            Assert.notNull(predicate, "predicate can not be null");
            return new Route(this.id, this.uri, this.order, predicate, this.gatewayFilters);
        }

        下面這個就是我們最重要的 Route 數(shù)據(jù)模型了,我們來看下是如何設計的吧。

        public class Route implements Ordered {
            // id,標識符,區(qū)別于其他 Route。
            private final String id;
            // destination uri,路由指向的目的地 uri,即客戶端請求最終被轉(zhuǎn)發(fā)的目的地。
            private final URI uri;
            // order,用于多個 Route 之間的排序,數(shù)值越小排序越靠前,匹配優(yōu)先級越高。
            private final int order;
            // predicate,謂語,表示匹配該 Route 的前置條件,即滿足相應的條件才會被路由到目的地 uri。
            private final AsyncPredicate<ServerWebExchange> predicate;
            // gateway filters,過濾器用于處理切面邏輯,如路由轉(zhuǎn)發(fā)前修改請求頭等。
            private final List<GatewayFilter> gatewayFilters;

            ...
        }

        到這里,外部化配置的 Route、Predicate、Filter 會被映射成 RouteDefinition、FilterDefinition、PredicateDefinition,而編碼方式會被映射成 Route、AsyncPredicate 、GatewayFilter。

        有時候我們可能又有外部化配置又有編碼方式的配置,那么這時候就需要有一個轉(zhuǎn)換。那接下來就看一下 Gateway 是如何轉(zhuǎn)換的。

        四、RouteDefinitionRouteLocator

        這里將 RouteDefinitionRouteLocator 單獨作為一小節(jié)來說,RouteDefinitionRouteLocator 將外部化配置的 RouteDefinition、FilterDefinition、PredicateDefinition 轉(zhuǎn)換成 Route、AsyncPredicate、GatewayFilter。

        上一篇我們講到的核心配置類 GatewayAutoConfiguration,給我提供了分析的入口,代碼如下:

        // ① GatewayProperties 
        // ② RouteDefinitionLocator 
        @Bean
        public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, List<GatewayFilterFactory> GatewayFilters, List<RoutePredicateFactory> predicates, RouteDefinitionLocator routeDefinitionLocator, @Qualifier("webFluxConversionService") ConversionService conversionService) {
            // ③ 這里進行轉(zhuǎn)換
            return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, GatewayFilters, properties, conversionService);
        }

        1、①、② 在上面外部化配置講過,這里就不重復闡述了。

        2、③ 外部化配置進行轉(zhuǎn)換,RouteDefinitionRouteLocator 是 RouteLocator 的實現(xiàn)

        public class RouteDefinitionRouteLocator implements RouteLocatorBeanFactoryAwareApplicationEventPublisherAware {
            protected final Log logger = LogFactory.getLog(this.getClass());
            public static final String DEFAULT_FILTERS = "defaultFilters";
            // RouteDefinition Locator,一個 RouteDefinitionLocator 對象。
            private final RouteDefinitionLocator routeDefinitionLocator;
            private final ConversionService conversionService;
            /**
                * predicates factories,Predicate 工廠列表,會被映射成 key 為 name, value 為 factory 的 Map。
             * 可以猜想出 gateway 是如何根據(jù) PredicateDefinition 中定義的 name 來匹配到相對應的 factory 了。
             * key :{@link RoutePredicateFactory#name()}
             */

            private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap();
            /**
             * filter factories,Gateway Filter 工廠列表,同樣會被映射成 key 為 name, value 為 factory 的 Map。
               * key :{@link GatewayFilterFactory#name()}
                */

            private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap();
            // gateway properties,外部化配置類。
            private final GatewayProperties gatewayProperties;
            private final SpelExpressionParser parser = new SpelExpressionParser();
            private BeanFactory beanFactory;
            private ApplicationEventPublisher publisher;
            @Autowired
            private Validator validator;

            public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator, List<RoutePredicateFactory> predicates, List<GatewayFilterFactory> gatewayFilterFactories, GatewayProperties gatewayProperties, ConversionService conversionService) {
                // 設置 RouteDefinitionLocator
                this.routeDefinitionLocator = routeDefinitionLocator;
                this.conversionService = conversionService;
                // ① 初始化 RoutePredicateFactory
                this.initFactories(predicates);
                // ② 初始化 gatewayFilterFactories
                gatewayFilterFactories.forEach((factory) -> {
                    GatewayFilterFactory var10000 = (GatewayFilterFactory)this.gatewayFilterFactories.put(factory.name(), factory);
                });
                // 設置 GatewayProperties
                this.gatewayProperties = gatewayProperties;
            }

            public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
                this.beanFactory = beanFactory;
            }

            public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
                this.publisher = publisher;
            }

            // ③ 實現(xiàn) RouteLocator 的 getRoutes() 方法 獲取 route
            public Flux<Route> getRoutes() {
                // 調(diào)用 convertToRoute 方法將 RouteDefinition 轉(zhuǎn)換成 Route。
                return this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute).map((route) -> {
                    if (this.logger.isDebugEnabled()) {
                        this.logger.debug("RouteDefinition matched: " + route.getId());
                    }

                    return route;
                });
            }

            ...
        }

        2.1 ① 初始化 RoutePredicateFactory

        private void initFactories(List<RoutePredicateFactory> predicates) {
            predicates.forEach((factory) -> {
                String key = factory.name();
                if (this.predicates.containsKey(key)) {
                    this.logger.warn("A RoutePredicateFactory named " + key + " already exists, class: " + this.predicates.get(key) + ". It will be overwritten.");
                }

                this.predicates.put(key, factory);
                if (this.logger.isInfoEnabled()) {
                    this.logger.info("Loaded RoutePredicateFactory [" + key + "]");
                }

            });
        }

        2.2 ② 初始化 gatewayFilterFactories

        // FilterDefinition 轉(zhuǎn)換成 GatewayFilter
        private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
            List<GatewayFilter> filters = new ArrayList();
            // ① 處理 GatewayProperties 中定義的默認的 FilterDefinition,轉(zhuǎn)換成 GatewayFilter。
            if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
                filters.addAll(this.loadGatewayFilters("defaultFilters"this.gatewayProperties.getDefaultFilters()));
            }
            // ② 將 RouteDefinition 中定義的 FilterDefinition 轉(zhuǎn)換成 GatewayFilter。
            if (!routeDefinition.getFilters().isEmpty()) {
                filters.addAll(this.loadGatewayFilters(routeDefinition.getId(), routeDefinition.getFilters()));
            }
            // ③ 對 GatewayFilter 進行排序,排序的詳細邏輯請查閱 spring 中的 Ordered 接口。
            AnnotationAwareOrderComparator.sort(filters);
            return filters;
        }

        2.3 ③ 實現(xiàn) RouteLocator 的 getRoutes() 方法 獲取 Route,真正轉(zhuǎn)換的方法

        public Flux<Route> getRoutes() {
            // 調(diào)用 convertToRoute 方法將 RouteDefinition 轉(zhuǎn)換成 Route。
            return this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute).map((route) -> {
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug("RouteDefinition matched: " + route.getId());
                }

                return route;
            });
        }

        private Route convertToRoute(RouteDefinition routeDefinition) {
            // 2.3.1 將 PredicateDefinition 轉(zhuǎn)換成 AsyncPredicate。
            AsyncPredicate<ServerWebExchange> predicate = this.combinePredicates(routeDefinition);
            // 2.3.2 將 FilterDefinition 轉(zhuǎn)換成 GatewayFilter。
            List<GatewayFilter> gatewayFilters = this.getFilters(routeDefinition);
            // 2.3.3 根據(jù) 1 和 2 兩步驟定義的變量生成 Route 對象。
            return ((AsyncBuilder)Route.async(routeDefinition).asyncPredicate(predicate).replaceFilters(gatewayFilters)).build();
        }

        2.3.1 將 PredicateDefinition 轉(zhuǎn)換成 AsyncPredicate

        private AsyncPredicate<ServerWebExchange> combinePredicates(RouteDefinition routeDefinition) {
            List<PredicateDefinition> predicates = routeDefinition.getPredicates();
            // ① 調(diào)用 lookup 方法,將列表中第一個 PredicateDefinition 轉(zhuǎn)換成 AsyncPredicate。
            AsyncPredicate<ServerWebExchange> predicate = this.lookup(routeDefinition, (PredicateDefinition)predicates.get(0));

            AsyncPredicate found;
            // ② 循環(huán)調(diào)用,將列表中每一個 PredicateDefinition 都轉(zhuǎn)換成 AsyncPredicate。
            // ③ 應用and操作,將所有的 AsyncPredicate 組合成一個 AsyncPredicate 對象。
            for(Iterator var4 = predicates.subList(1, predicates.size()).iterator(); var4.hasNext(); predicate = predicate.and(found)) {
                PredicateDefinition andPredicate = (PredicateDefinition)var4.next();
                found = this.lookup(routeDefinition, andPredicate);
            }

            return predicate;
        }

        2.3.2 將 FilterDefinition 轉(zhuǎn)換成 GatewayFilter

        // FilterDefinition 轉(zhuǎn)換成 GatewayFilter
        private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
            List<GatewayFilter> filters = new ArrayList();
            // ① 處理 GatewayProperties 中定義的默認的 FilterDefinition,轉(zhuǎn)換成 GatewayFilter。
            if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
                filters.addAll(this.loadGatewayFilters("defaultFilters"this.gatewayProperties.getDefaultFilters()));
            }
            // ② 將 RouteDefinition 中定義的 FilterDefinition 轉(zhuǎn)換成 GatewayFilter。
            if (!routeDefinition.getFilters().isEmpty()) {
                filters.addAll(this.loadGatewayFilters(routeDefinition.getId(), routeDefinition.getFilters()));
            }
            // ③ 對 GatewayFilter 進行排序,排序的詳細邏輯請查閱 spring 中的 Ordered 接口。
            AnnotationAwareOrderComparator.sort(filters);
            return filters;
        }

        2.3.3 根據(jù) 2.3.1 和 2.3.2 兩步驟定義的變量生成 Route 對象

        public Route build() {
            Assert.notNull(this.id, "id can not be null");
            Assert.notNull(this.uri, "uri can not be null");
            AsyncPredicate<ServerWebExchange> predicate = this.getPredicate();
            Assert.notNull(predicate, "predicate can not be null");
            return new Route(this.id, this.uri, this.order, predicate, this.gatewayFilters);
        }

        到這里我們就已經(jīng)知道外部化配置配合是如何轉(zhuǎn)換的,那么現(xiàn)在 Route 已經(jīng)組裝完畢了,現(xiàn)在就是看一下 Route 里面的 Predicate 和 Filter 的實現(xiàn)。

        五、總結(jié)

        • Route 構(gòu)建方式有兩種方式:外部化配置和編程方式。

        • 通過 RouteDefinitionLocator 的各種實現(xiàn),來多樣化的獲取不同的外置配置。

        • 既有外部化配置又有編碼方式的配置,那么這時候就需要有一個轉(zhuǎn)換根據(jù) RouteDefinitionRouteLocator 把這些外置配置轉(zhuǎn)存成 Route。




        歡迎大家關(guān)注我的公眾號【老周聊架構(gòu)】,Java后端主流技術(shù)棧的原理、源碼分析、架構(gòu)以及各種互聯(lián)網(wǎng)高并發(fā)、高性能、高可用的解決方案。

        喜歡的話,點贊、再看、分享三連。



        點個在看你最好看



        瀏覽 230
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            免费人成网站www 549tv | 女人被添荫蒂视频y | 欲色无码 | 免费看黄在线网站 | 一边摸一边舔 | 99精品中文字幕 | 舒淇徐锦江三级做爰 | 插插插插视频 | 日韩a在线观看 | 黄色三级片在线观看 |