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

點(diǎn)擊上方老周聊架構(gòu)關(guān)注我
一、前言
我們上一篇講了:Spring Cloud Gateway 源碼剖析之配置初始化,通過(guò)自動(dòng)加載初始化六個(gè)配置實(shí)例,Spring Cloud Gateway 就完成自身的加載和初始化工作。我們知道 Gateway 的核心是路由加過(guò)濾,既然網(wǎng)關(guān)相關(guān)初始化工作做好了,那得開始路由相關(guān)的工作了。
接下來(lái)我們就來(lái)分析下平時(shí)在 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 // ④
① 配置了一個(gè) Route id 為 after_route
② 客戶端請(qǐng)求轉(zhuǎn)發(fā)的目的地:https://example.org
③ 在 request 中,當(dāng)存在名字 mycookie 的 cookie 的值匹配 mycookievalue 則算成功
④ 定義了一個(gè) Filter,匹配成功后,會(huì)在請(qǐng)求頭上添加 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();
}簡(jiǎn)單介紹了構(gòu)建 Route 的兩種方式,下面分析 Route 是如何把外部化配置與編碼配置之間進(jìn)行轉(zhuǎn)換。
三、Route 構(gòu)建原理
1、外部化配置
外部化配置是通過(guò) GatewayProperties 進(jìn)行構(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();
/**
* 默認(rèn)的過(guò)濾器定義列表
* 加載配置 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 列表
* 默認(rèn)包含{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
用來(lái)對(duì) Route 進(jìn)行定義。也就是,通過(guò) GatewayProperties 會(huì)與外部化配置進(jìn)行綁定,把外部化配置比如 properties 或者 yml 綁定到 GatewayProperties 中。
/**
* 路由定義實(shí)體信息,包含路由的定義信息
*/
@Validated
public class RouteDefinition {
/**
* 路由ID 編號(hào),唯一
*/
@NotEmpty
private String id = UUID.randomUUID().toString();
/**
* 謂語(yǔ)定義數(shù)組
* predicates 屬性,謂語(yǔ)定義數(shù)組
* 請(qǐng)求通過(guò) 判斷是否匹配。在 Route 里,PredicateDefinition 轉(zhuǎn)換成 Predicate
*/
@NotEmpty
@Valid
private List<PredicateDefinition> predicates = new ArrayList();
/**
* 過(guò)濾器定義數(shù)組
* filters 屬性,過(guò)濾器定義數(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
/**
* 謂語(yǔ)定義,在 Route 里,PredicateDefinition 將轉(zhuǎn)換成 Predicate
*/
@Validated
public class PredicateDefinition {
/**
* 謂語(yǔ)定義名字
* 通過(guò) name 對(duì)應(yīng)到 org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory 的實(shí)現(xiàn)類。
* 例如: name=Query 對(duì)應(yīng)到 QueryRoutePredicateFactory
*/
@NotNull
private String name;
/**
* 參數(shù)數(shù)組
* 例如,name=Host / args={"_genkey_0" : "iocoder.cn"} ,匹配請(qǐng)求的 hostname 為 iocoder.cn
*/
private Map<String, String> args = new LinkedHashMap();
...
}1.3 FilterDefinition
/**
* 過(guò)濾器定義,在 Route 里,F(xiàn)ilterDefinition將轉(zhuǎn)換成 GatewayFilter
*/
@Validated
public class FilterDefinition {
/**
* 過(guò)濾器定義名字
* 通過(guò) name 對(duì)應(yīng)到 org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory 的實(shí)現(xiàn)類。
* 例如,name=AddRequestParameter 對(duì)應(yīng)到 AddRequestParameterGatewayFilterFactory
*/
@NotNull
private String name;
/**
* 參數(shù)數(shù)組
* 例如 name=AddRequestParameter / args={"_genkey_0": "foo", "_genkey_1": "bar"} ,添加請(qǐng)求參數(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 提供多種方式來(lái)獲取外部的配置,而 RouteDefinitionLocator 就是父接口,下面有多種實(shí)現(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
從存儲(chǔ)器(內(nèi)存、Redis、MySQL 等)讀取、保存、刪除路由配置。InMemoryRouteDefinitionRepository 是基于內(nèi)存的。
public class InMemoryRouteDefinitionRepository implements RouteDefinitionRepository {
/**
* 路由配置映射 通過(guò)此來(lái)保存route
* key :路由編號(hào) {@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)存,通過(guò) Map
1.4.3 DiscoveryClientRouteDefinitionLocator
獲取在注冊(cè)中心的服務(wù)列表,生成對(duì)應(yīng)的 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ā)起請(qǐng)求
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();
// 設(shè)置 ID
routeDefinition.setId(this.routeIdPrefix + serviceId);
// 設(shè)置 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 重寫過(guò)濾器
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 注冊(cè)中心自行調(diào)試:
// 開啟Eureka
@EnableDiscoveryClient
public class GatewaySampleApplication {
// ... 省略其他代碼
@Bean
public RouteDefinitionLocator discoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient) {
return new DiscoveryClientRouteDefinitionLocator(discoveryClient);
}
}當(dāng)然要自己加入 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 的實(shí)現(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);
}
}到此為止外部化的配置的多種方式全部解析完畢。接下來(lái)我們來(lái)看下編程方式。
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)建多個(gè)路由信息。
return builder.routes()
// ② 指定了 Predicates,這里包含兩個(gè):
// 請(qǐng)求頭Host需要匹配**.abc.org,通過(guò) HostRoutePredicateFactory 產(chǎn)生。
// 請(qǐng)求路徑需要匹配/anything/png,通過(guò) PathRoutePredicateFactory 產(chǎn)生。
.route(r -> r.host("**.abc.org").and().path("/anything/png")
// ③ 指定了一個(gè) Filter,下游服務(wù)響應(yīng)后添加響應(yīng)頭X-TestHeader:foobar,
// 通過(guò) 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實(shí)例
.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 實(shí)例
// 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 對(duì)象,它是 Route 的集合所以上面 ① 中的 build 方法就是對(duì)應(yīng)的 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);
}
下面這個(gè)就是我們最重要的 Route 數(shù)據(jù)模型了,我們來(lái)看下是如何設(shè)計(jì)的吧。
public class Route implements Ordered {
// id,標(biāo)識(shí)符,區(qū)別于其他 Route。
private final String id;
// destination uri,路由指向的目的地 uri,即客戶端請(qǐng)求最終被轉(zhuǎn)發(fā)的目的地。
private final URI uri;
// order,用于多個(gè) Route 之間的排序,數(shù)值越小排序越靠前,匹配優(yōu)先級(jí)越高。
private final int order;
// predicate,謂語(yǔ),表示匹配該 Route 的前置條件,即滿足相應(yīng)的條件才會(huì)被路由到目的地 uri。
private final AsyncPredicate<ServerWebExchange> predicate;
// gateway filters,過(guò)濾器用于處理切面邏輯,如路由轉(zhuǎn)發(fā)前修改請(qǐng)求頭等。
private final List<GatewayFilter> gatewayFilters;
...
}
到這里,外部化配置的 Route、Predicate、Filter 會(huì)被映射成 RouteDefinition、FilterDefinition、PredicateDefinition,而編碼方式會(huì)被映射成 Route、AsyncPredicate 、GatewayFilter。
有時(shí)候我們可能又有外部化配置又有編碼方式的配置,那么這時(shí)候就需要有一個(gè)轉(zhuǎn)換。那接下來(lái)就看一下 Gateway 是如何轉(zhuǎn)換的。
四、RouteDefinitionRouteLocator
這里將 RouteDefinitionRouteLocator 單獨(dú)作為一小節(jié)來(lái)說(shuō),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) {
// ③ 這里進(jìn)行轉(zhuǎn)換
return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, GatewayFilters, properties, conversionService);
}
1、①、② 在上面外部化配置講過(guò),這里就不重復(fù)闡述了。
2、③ 外部化配置進(jìn)行轉(zhuǎn)換,RouteDefinitionRouteLocator 是 RouteLocator 的實(shí)現(xiàn)
public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {
protected final Log logger = LogFactory.getLog(this.getClass());
public static final String DEFAULT_FILTERS = "defaultFilters";
// RouteDefinition Locator,一個(gè) RouteDefinitionLocator 對(duì)象。
private final RouteDefinitionLocator routeDefinitionLocator;
private final ConversionService conversionService;
/**
* predicates factories,Predicate 工廠列表,會(huì)被映射成 key 為 name, value 為 factory 的 Map。
* 可以猜想出 gateway 是如何根據(jù) PredicateDefinition 中定義的 name 來(lái)匹配到相對(duì)應(yīng)的 factory 了。
* key :{@link RoutePredicateFactory#name()}
*/
private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap();
/**
* filter factories,Gateway Filter 工廠列表,同樣會(huì)被映射成 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) {
// 設(shè)置 RouteDefinitionLocator
this.routeDefinitionLocator = routeDefinitionLocator;
this.conversionService = conversionService;
// ① 初始化 RoutePredicateFactory
this.initFactories(predicates);
// ② 初始化 gatewayFilterFactories
gatewayFilterFactories.forEach((factory) -> {
GatewayFilterFactory var10000 = (GatewayFilterFactory)this.gatewayFilterFactories.put(factory.name(), factory);
});
// 設(shè)置 GatewayProperties
this.gatewayProperties = gatewayProperties;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
// ③ 實(shí)現(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 中定義的默認(rèn)的 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()));
}
// ③ 對(duì) GatewayFilter 進(jìn)行排序,排序的詳細(xì)邏輯請(qǐng)查閱 spring 中的 Ordered 接口。
AnnotationAwareOrderComparator.sort(filters);
return filters;
}
2.3 ③ 實(shí)現(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 對(duì)象。
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 方法,將列表中第一個(gè) PredicateDefinition 轉(zhuǎn)換成 AsyncPredicate。
AsyncPredicate<ServerWebExchange> predicate = this.lookup(routeDefinition, (PredicateDefinition)predicates.get(0));
AsyncPredicate found;
// ② 循環(huán)調(diào)用,將列表中每一個(gè) PredicateDefinition 都轉(zhuǎn)換成 AsyncPredicate。
// ③ 應(yīng)用and操作,將所有的 AsyncPredicate 組合成一個(gè) AsyncPredicate 對(duì)象。
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 中定義的默認(rèn)的 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()));
}
// ③ 對(duì) GatewayFilter 進(jìn)行排序,排序的詳細(xì)邏輯請(qǐng)查閱 spring 中的 Ordered 接口。
AnnotationAwareOrderComparator.sort(filters);
return filters;
}
2.3.3 根據(jù) 2.3.1 和 2.3.2 兩步驟定義的變量生成 Route 對(duì)象
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 的實(shí)現(xiàn)。
五、總結(jié)
Route 構(gòu)建方式有兩種方式:外部化配置和編程方式。
通過(guò) RouteDefinitionLocator 的各種實(shí)現(xiàn),來(lái)多樣化的獲取不同的外置配置。
既有外部化配置又有編碼方式的配置,那么這時(shí)候就需要有一個(gè)轉(zhuǎn)換根據(jù) RouteDefinitionRouteLocator 把這些外置配置轉(zhuǎn)存成 Route。
歡迎大家關(guān)注我的公眾號(hào)【老周聊架構(gòu)】,Java后端主流技術(shù)棧的原理、源碼分析、架構(gòu)以及各種互聯(lián)網(wǎng)高并發(fā)、高性能、高可用的解決方案。
喜歡的話,點(diǎn)贊、再看、分享三連。

點(diǎn)個(gè)在看你最好看
