一、前言
我们上一篇讲了: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 RouteDefinitionLocator, ApplicationListener<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 RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware { 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后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。
喜欢的话,点赞、再看、分享三连。