这次先不聊 SAAS 也不聊教育,先聊聊实际的问题:
先回答标题:由 RouteDefinitionRouteLocator 实现路由策略的管理,并提供检索
RouteDefinitionRouteLocator 在 其构造方法中会加载所有的路由器(称 Routers),这些路由器都是以 filter (GatewayFilter)的形式工作的, 但是这些 filter 它不是直接写好的类, 而是由工厂生产出来例如:
RewritePathGatewayFilterFactory, 通过 apply 方法生产出一个 GatewayFilter,其内部实现了重写 path 的逻辑,再例如:
RequestHeaderToRequestUriGatewayFilterFactory, 通过 apply 方法生产出来一个能通过地址路由到新地址的 GatewayFilter。
所以 RouteDefinitionRouteLocator 中维护的其实是一组 GatewayFilter 的各种工厂(工厂模式)。
可以读下原码:
public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,
List<RoutePredicateFactory> predicates,
List<GatewayFilterFactory> gatewayFilterFactories,
GatewayProperties gatewayProperties,
ConfigurationService configurationService) {
this.routeDefinitionLocator = routeDefinitionLocator;
this.configurationService = configurationService;
initFactories(predicates);
gatewayFilterFactories.forEach(
factory -> this.gatewayFilterFactories.put(factory.name(), factory));
this.gatewayProperties = gatewayProperties;
}
它将 List<GatewayFilterFactory> gatewayFilterFactories, 利用 map 建了索引 (Map<String, GatewayFilterFactory> gatewayFilterFactories)。后面再看看它是如何使用的。
为何标题叫 : 路由到“路由策略”?
因为当请求过来的时候 gateway 会根据请求特征,先找到这个请求需要有哪些 GatewayFilter 来加工它,所以它得先从这一众 GatewayFilterFactory 中找到这些目标工厂,最终得到的是一组 GatewayFilterFactory 集合(只要调用它们的 apply 方法就能得到对应的 Filter),所以我称这组集合为 “路由策略” ,然后不用想这组策略应该会被交给 一个 FilterChain 去执行,执行的是对我们原请求的一系列路由管理策略例如 把路径 /xx 的请求转发到 /xxxzzz,把路径 /ssss 替换成 /yyy,或者是其他的自定义路由器。这是它的原理,我们可以看看 RouteDefinitionRouteLocator.getRoutes 方法(主要看 loadGatewayFilters):
@Override
public Flux<Route> getRoutes() {
return this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute)
// TODO: error handling
.map(route -> {
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition matched: " + route.getId());
}
return route;
});
/*
* TODO: trace logging if (logger.isTraceEnabled()) {
* logger.trace("RouteDefinition did not match: " + routeDefinition.getId()); }
*/
}
private Route convertToRoute(RouteDefinition routeDefinition) {
AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);
return Route.async(routeDefinition).asyncPredicate(predicate)
.replaceFilters(gatewayFilters).build();
}
private AsyncPredicate<ServerWebExchange> combinePredicates(
RouteDefinition routeDefinition) {
List<PredicateDefinition> predicates = routeDefinition.getPredicates();
AsyncPredicate<ServerWebExchange> predicate = lookup(routeDefinition,
predicates.get(0));
for (PredicateDefinition andPredicate : predicates.subList(1,
predicates.size())) {
AsyncPredicate<ServerWebExchange> found = lookup(routeDefinition,
andPredicate);
predicate = predicate.and(found);
}
return predicate;
}
private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
List<GatewayFilter> filters = new ArrayList<>();
// TODO: support option to apply defaults after route specific filters?
if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
filters.addAll(loadGatewayFilters(DEFAULT_FILTERS,
this.gatewayProperties.getDefaultFilters()));
}
if (!routeDefinition.getFilters().isEmpty()) {
filters.addAll(loadGatewayFilters(routeDefinition.getId(),
routeDefinition.getFilters()));
}
AnnotationAwareOrderComparator.sort(filters);
return filters;
}
@SuppressWarnings("unchecked")
List<GatewayFilter> loadGatewayFilters(String id,
List<FilterDefinition> filterDefinitions) {
ArrayList<GatewayFilter> ordered = new ArrayList<>(filterDefinitions.size());
for (int i = 0; i < filterDefinitions.size(); i++) {
FilterDefinition definition = filterDefinitions.get(i);
GatewayFilterFactory factory = this.gatewayFilterFactories
.get(definition.getName());
if (factory == null) {
throw new IllegalArgumentException(
"Unable to find GatewayFilterFactory with name "
+ definition.getName());
}
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition " + id + " applying filter "
+ definition.getArgs() + " to " + definition.getName());
}
// @formatter:off
Object configuration = this.configurationService.with(factory)
.name(definition.getName())
.properties(definition.getArgs())
.eventFunction((bound, properties) -> new FilterArgsEvent(
// TODO: why explicit cast needed or java compile fails
RouteDefinitionRouteLocator.this, id, (Map<String, Object>) properties))
.bind();
// @formatter:on
// some filters require routeId
// TODO: is there a better place to apply this?
if (configuration instanceof HasRouteId) {
HasRouteId hasRouteId = (HasRouteId) configuration;
hasRouteId.setRouteId(id);
}
GatewayFilter gatewayFilter = factory.apply(configuration);
if (gatewayFilter instanceof Ordered) {
ordered.add(gatewayFilter);
}
else {
ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));
}
}
return ordered;
}
loadGatewayFilters 是从预建的 map 索引中,根据 id 来路由到对应的 factory, 再调 factory.apply 方法得到这个 Filter,然后添加到 ArrayList<GatewayFilter> ordered 中, 这 ordered 就是我们本文的核心 :路由策略。
小结下:通过初始化的时候加载到所有的工厂,并按服务名称建议工厂索引,用到它的时候,再通过 getRoutes 方法从索引中取出路由策略,然后交给 FilterChain。涉及到的设计模式:工厂模式、责任链、面向接口的编程思想,还可以把 react 也提下,可能还有建造者模式。