Spring Cloud Gateway 源码剖析之Route数据模型
点击上方老周聊架构关注我
一、前言
我们上一篇讲了: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
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后端主流技术栈的原理、源码分析、架构以及各种互联网高并发、高性能、高可用的解决方案。
喜欢的话,点赞、再看、分享三连。
点个在看你最好看