Spring Cloud Gateway 源码剖析之Route数据模型

老周聊架构

共 64493字,需浏览 129分钟

 ·

2021-04-02 12:39

点击上方老周聊架构关注我


一、前言

我们上一篇讲了:Spring Cloud Gateway 源码剖析之配置初始化,通过自动加载初始化六个配置实例,Spring Cloud Gateway 就完成自身的加载和初始化工作。我们知道 Gateway 的核心是路由加过滤,既然网关相关初始化工作做好了,那得开始路由相关的工作了。

接下来我们就来分析下平时在 properties 或者 yml 中配置的有关 Gateway 的配置是如何构建成 Route 的。

二、Route 构建方式

一般构建分为两种:外部化配置和编程方式

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

  • ② 客户端请求转发的目的地:https://example.org

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

  • ④ 定义了一个 Filter,匹配成功后,会在请求头上添加 X-Request-Foo:Bar

2、编程方式

用上面的例子,转换成编程方式

@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();
}

简单介绍了构建 Route 的两种方式,下面分析 Route 是如何把外部化配置与编码配置之间进行转换。

三、Route 构建原理

1、外部化配置

外部化配置是通过 GatewayProperties 进行构建的

/**
 * 网关配置信息加载
 * 从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();

    /**
     * 网媒体类型列表
     * 加载配置 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();

    /**
     * 谓语定义数组
     * predicates 属性,谓语定义数组
     * 请求通过   判断是否匹配。在 Route 里,PredicateDefinition 转换成 Predicate
     */

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

    /**
     * 过滤器定义数组
     * filters 属性,过滤器定义数组。
     * 在 Route 里,FilterDefinition 转换成 GatewayFilter
     */

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

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

    @NotNull
    private URI uri;

    /**
     * 顺序
     */

    private int order = 0;

    ...
}

1.2 PredicateDefinition

/**
 * 谓语定义,在 Route 里,PredicateDefinition 将转换成 Predicate
 */

@Validated
public class PredicateDefinition {
    /**
     * 谓语定义名字
     * 通过 name 对应到 org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory 的实现类。
     * 例如: name=Query 对应到 QueryRoutePredicateFactory
     */

    @NotNull
    private String name;

    /**
     * 参数数组
     * 例如,name=Host / args={"_genkey_0" : "iocoder.cn"} ,匹配请求的 hostname 为 iocoder.cn
     */

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

    ...
}

1.3 FilterDefinition

/**
 * 过滤器定义,在 Route 里,FilterDefinition将转换成 GatewayFilter
 */

@Validated
public class FilterDefinition {
    /**
     * 过滤器定义名字
     * 通过 name 对应到 org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory 的实现类。
     * 例如,name=AddRequestParameter 对应到 AddRequestParameterGatewayFilterFactory
     */

    @NotNull
    private String name;

    /**
     * 参数数组
     * 例如 name=AddRequestParameter / args={"_genkey_0": "foo", "_genkey_1": "bar"} ,添加请求参数 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 就是父接口,下面有多种实现。

在这里插入图片描述

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 获取路由配置数组。
        return Flux.fromIterable(this.properties.getRoutes());
    }
}

1.4.2 InMemoryRouteDefinitionRepository

从存储器(内存、Redis、MySQL 等)读取、保存、删除路由配置。InMemoryRouteDefinitionRepository 是基于内存的。

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

基于内存,通过 Map routes 来保存 Route,缺点是如果重启,那么 route 会丢失。可以实现 RouteDefinitionRepository 接口自定义比如通过 Redis、Mysql 等来保存 Route。

1.4.3 DiscoveryClientRouteDefinitionLocator

获取在注册中心的服务列表,生成对应的 RouteDefinition 数组。

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,然后发起请求
        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 注册中心自行调试:

// 开启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 的实现,为 RouteDefinitionRouteLocator 提供统一入口。

public class CompositeRouteDefinitionLocator implements RouteDefinitionLocator {
    // RouteDefinitionLocator 数组
    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 可以构建多个路由信息。
        return builder.routes()
                // ② 指定了 Predicates,这里包含两个:
                // 请求头Host需要匹配**.abc.org,通过 HostRoutePredicateFactory 产生。
                // 请求路径需要匹配/anything/png,通过 PathRoutePredicateFactory 产生。
                .route(r -> r.host("**.abc.org").and().path("/anything/png")
                    // ③ 指定了一个 Filter,下游服务响应后添加响应头X-TestHeader:foobar,
                    // 通过 AddResponseHeaderGatewayFilterFactory 产生。
                    .filters(f ->
                            f.prefixPath("/httpbin")
                                    .addResponseHeader("X-TestHeader""foobar"))
                     // ④ 指定路由转发的目的地 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)
                )
                // ⑤ 创建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 模式构建 Route

  • ② 创建 Predicates

  • ③ 创建 Filter

  • ④ 需要转发的目的地 uri

  • ⑤ 创建 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 数据模型了,我们来看下是如何设计的吧。

public class Route implements Ordered {
    // id,标识符,区别于其他 Route。
    private final String id;
    // destination uri,路由指向的目的地 uri,即客户端请求最终被转发的目的地。
    private final URI uri;
    // order,用于多个 Route 之间的排序,数值越小排序越靠前,匹配优先级越高。
    private final int order;
    // predicate,谓语,表示匹配该 Route 的前置条件,即满足相应的条件才会被路由到目的地 uri。
    private final AsyncPredicate<ServerWebExchange> predicate;
    // gateway filters,过滤器用于处理切面逻辑,如路由转发前修改请求头等。
    private final List<GatewayFilter> gatewayFilters;

    ...
}

到这里,外部化配置的 Route、Predicate、Filter 会被映射成 RouteDefinition、FilterDefinition、PredicateDefinition,而编码方式会被映射成 Route、AsyncPredicate 、GatewayFilter。

有时候我们可能又有外部化配置又有编码方式的配置,那么这时候就需要有一个转换。那接下来就看一下 Gateway 是如何转换的。

四、RouteDefinitionRouteLocator

这里将 RouteDefinitionRouteLocator 单独作为一小节来说,RouteDefinitionRouteLocator 将外部化配置的 RouteDefinition、FilterDefinition、PredicateDefinition 转换成 Route、AsyncPredicate、GatewayFilter。

上一篇我们讲到的核心配置类 GatewayAutoConfiguration,给我提供了分析的入口,代码如下:

// ① GatewayProperties 
// ② RouteDefinitionLocator 
@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, List<GatewayFilterFactory> GatewayFilters, List<RoutePredicateFactory> predicates, RouteDefinitionLocator routeDefinitionLocator, @Qualifier("webFluxConversionService") ConversionService conversionService) {
    // ③ 这里进行转换
    return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, GatewayFilters, properties, conversionService);
}

1、①、② 在上面外部化配置讲过,这里就不重复阐述了。

2、③ 外部化配置进行转换,RouteDefinitionRouteLocator 是 RouteLocator 的实现

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 是如何根据 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;
    }

    // ③ 实现 RouteLocator 的 getRoutes() 方法 获取 route
    public Flux<Route> getRoutes() {
        // 调用 convertToRoute 方法将 RouteDefinition 转换成 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 转换成 GatewayFilter
private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
    List<GatewayFilter> filters = new ArrayList();
    // ① 处理 GatewayProperties 中定义的默认的 FilterDefinition,转换成 GatewayFilter。
    if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
        filters.addAll(this.loadGatewayFilters("defaultFilters"this.gatewayProperties.getDefaultFilters()));
    }
    // ② 将 RouteDefinition 中定义的 FilterDefinition 转换成 GatewayFilter。
    if (!routeDefinition.getFilters().isEmpty()) {
        filters.addAll(this.loadGatewayFilters(routeDefinition.getId(), routeDefinition.getFilters()));
    }
    // ③ 对 GatewayFilter 进行排序,排序的详细逻辑请查阅 spring 中的 Ordered 接口。
    AnnotationAwareOrderComparator.sort(filters);
    return filters;
}

2.3 ③ 实现 RouteLocator 的 getRoutes() 方法 获取 Route,真正转换的方法

public Flux<Route> getRoutes() {
    // 调用 convertToRoute 方法将 RouteDefinition 转换成 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 转换成 AsyncPredicate。
    AsyncPredicate<ServerWebExchange> predicate = this.combinePredicates(routeDefinition);
    // 2.3.2 将 FilterDefinition 转换成 GatewayFilter。
    List<GatewayFilter> gatewayFilters = this.getFilters(routeDefinition);
    // 2.3.3 根据 1 和 2 两步骤定义的变量生成 Route 对象。
    return ((AsyncBuilder)Route.async(routeDefinition).asyncPredicate(predicate).replaceFilters(gatewayFilters)).build();
}

2.3.1 将 PredicateDefinition 转换成 AsyncPredicate

private AsyncPredicate<ServerWebExchange> combinePredicates(RouteDefinition routeDefinition) {
    List<PredicateDefinition> predicates = routeDefinition.getPredicates();
    // ① 调用 lookup 方法,将列表中第一个 PredicateDefinition 转换成 AsyncPredicate。
    AsyncPredicate<ServerWebExchange> predicate = this.lookup(routeDefinition, (PredicateDefinition)predicates.get(0));

    AsyncPredicate found;
    // ② 循环调用,将列表中每一个 PredicateDefinition 都转换成 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 转换成 GatewayFilter

// FilterDefinition 转换成 GatewayFilter
private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
    List<GatewayFilter> filters = new ArrayList();
    // ① 处理 GatewayProperties 中定义的默认的 FilterDefinition,转换成 GatewayFilter。
    if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
        filters.addAll(this.loadGatewayFilters("defaultFilters"this.gatewayProperties.getDefaultFilters()));
    }
    // ② 将 RouteDefinition 中定义的 FilterDefinition 转换成 GatewayFilter。
    if (!routeDefinition.getFilters().isEmpty()) {
        filters.addAll(this.loadGatewayFilters(routeDefinition.getId(), routeDefinition.getFilters()));
    }
    // ③ 对 GatewayFilter 进行排序,排序的详细逻辑请查阅 spring 中的 Ordered 接口。
    AnnotationAwareOrderComparator.sort(filters);
    return filters;
}

2.3.3 根据 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);
}

到这里我们就已经知道外部化配置配合是如何转换的,那么现在 Route 已经组装完毕了,现在就是看一下 Route 里面的 Predicate 和 Filter 的实现。

五、总结

  • Route 构建方式有两种方式:外部化配置和编程方式。

  • 通过 RouteDefinitionLocator 的各种实现,来多样化的获取不同的外置配置。

  • 既有外部化配置又有编码方式的配置,那么这时候就需要有一个转换根据 RouteDefinitionRouteLocator 把这些外置配置转存成 Route。




欢迎大家关注我的公众号【老周聊架构】,Java后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。

喜欢的话,点赞、再看、分享三连。



点个在看你最好看



浏览 195
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报