先看看gateway是怎麼獲取我們配置的路由:
在gateway啟動時,GatewayAutoConfiguration幫我們註冊了一系列beans
@Bean
@Primary
public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {
//這裡注入所有RouteDefinitionLocator,我們在配置檔案裡配置的路由就是通過子類PropertiesRouteDefinitionLocator來完成的,
//可以參考InMemoryRouteDefinitionRepository實現自己的RouteDefinition的儲存,RouteDefinition會在後面轉換成Route
return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));
}
//接著看CompositeRouteDefinitionLocator類裡的獲取路由定義方法:
public Flux<RouteDefinition> getRouteDefinitions() {
//即輪訓所有RouteDefinitionLocator的市現率呼叫getRouteDefinitions,這樣就把所有RouteDefinition整合到一起了
return this.delegates.flatMap(RouteDefinitionLocator::getRouteDefinitions);
}
//接著看GatewayAutoConfiguration,剛才的bean RouteDefinitionLocator 作為引數注入到routeDefinitionLocator
@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
List<GatewayFilterFactory> GatewayFilters,
List<RoutePredicateFactory> predicates,
RouteDefinitionLocator routeDefinitionLocator) {
return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, GatewayFilters, properties);
}
//看RouteDefinitionRouteLocator類:
public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {
······
public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,
List<RoutePredicateFactory> predicates,
List<GatewayFilterFactory> gatewayFilterFactories,
GatewayProperties gatewayProperties) {
this.routeDefinitionLocator = routeDefinitionLocator;
//RoutePredicateFactory放到map裡,去掉RoutePredicateFactory字尾,所以配置時就可以寫成
//predicates:
// - Path=/abc/*
//的形式而不用寫完整的PathRoutePredicateFactory
initFactories(predicates);
//把bean名字裡的GatewayFilterFactory去掉,所以我們配置檔案裡配置時也要把GatewayFilterFactory去掉,不然會找不到
gatewayFilterFactories.forEach(factory -> this.gatewayFilterFactories.put(factory.name(), factory));
this.gatewayProperties = gatewayProperties;
}
······
@Override
public Flux<Route> getRoutes() {
return this.routeDefinitionLocator.getRouteDefinitions()
.map(this::convertToRoute)//獲取所有的RouteDefinition後轉為Route
//TODO: error handling
.map(route -> {
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition matched: " + route.getId());
}
return route;
});
}
private Route convertToRoute(RouteDefinition routeDefinition) {
//獲取所有匹配規則,所有的規則都滿足才走對應Route
AsyncPredicate<ServerWebExchange> predicate = combinePredicates(routeDefinition);
//獲取全部過濾器
List<GatewayFilter> gatewayFilters = getFilters(routeDefinition);
return Route.async(routeDefinition)
.asyncPredicate(predicate)
.replaceFilters(gatewayFilters)
.build();
}
private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
List<GatewayFilter> filters = new ArrayList<>();
//把全域性過濾器新增到當前路由,loadGatewayFilters呼叫GatewayFilterFactory裡的apply配置類裡面的靜態Config類,並且把沒實現Ordered介面的類用OrderedGatewayFilter代理一下,方便下面排序
if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
filters.addAll(loadGatewayFilters("defaultFilters",
this.gatewayProperties.getDefaultFilters()));
}
//新增本條路由定義的過濾器
if (!routeDefinition.getFilters().isEmpty()) {
filters.addAll(loadGatewayFilters(routeDefinition.getId(), routeDefinition.getFilters()));
}
//排序
AnnotationAwareOrderComparator.sort(filters);
return filters;
}
private AsyncPredicate<ServerWebExchange> combinePredicates(RouteDefinition routeDefinition) {
//獲取路由的判斷條件,比如我們大多是根據url判斷,用的是PathRoutePredicateFactory
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);//如果有多個匹配規則就要滿足所有的才可以
}
//返回的結果會在RoutePredicateHandlerMapping類裡使用apply方法呼叫
return predicate;
}
@SuppressWarnings("unchecked")
private AsyncPredicate<ServerWebExchange> lookup(RouteDefinition route, PredicateDefinition predicate) {
RoutePredicateFactory<Object> factory = this.predicates.get(predicate.getName());
if (factory == null) {
throw new IllegalArgumentException("Unable to find RoutePredicateFactory with name " + predicate.getName());
}
Map<String, String> args = predicate.getArgs();
if (logger.isDebugEnabled()) {
logger.debug("RouteDefinition " + route.getId() + " applying "
+ args + " to " + predicate.getName());
}
Map<String, Object> properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory);
//獲取RoutePredicateFactory實現類裡的靜態Config
Object config = factory.newConfig();
ConfigurationUtils.bind(config, properties,
factory.shortcutFieldPrefix(), predicate.getName(), validator);
if (this.publisher != null) {
this.publisher.publishEvent(new PredicateArgsEvent(this, route.getId(), properties));
}
//這裡實際呼叫的是ServerWebExchangeUtils類裡的toAsyncPredicate方法,並配置的config
return factory.applyAsync(config);
}
}
// 把剛才的bean RouteDefinitionRouteLocator作為引數注入到routeLocators
@Bean
@Primary
public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
//這裡CompositeRouteLocator與上面的CompositeRouteDefinitionLocator類似,也是遍歷routeLocators呼叫getRoutes方法整合所有Route
return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
}
//接著看CachingRouteLocator類
public class CachingRouteLocator implements RouteLocator {
private final RouteLocator delegate;
private final Flux<Route> routes;
private final Map<String, List> cache = new HashMap<>();
public CachingRouteLocator(RouteLocator delegate) {
this.delegate = delegate;
routes = CacheFlux.lookup(cache, "routes", Route.class)
//每次請求都會獲取routes,每當拿快取又找不到的時候(即我們修改了路由),就會呼叫上面CompositeRouteLocator的getRoutes方法重新獲取路由
.onCacheMissResume(() -> this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE));
}
·······
public Flux<Route> refresh() {
this.cache.clear();//清掉路由快取
return this.routes;
}
@EventListener(RefreshRoutesEvent.class)
/* for testing */ void handleRefresh() {//更新路由用的是spring事件機制
refresh();
}
}
//接著看GatewayAutoConfiguration,剛才的bean CachingRouteLocator作為引數注入到RouteLocator
@Bean
public RoutePredicateHandlerMapping routePredicateHandlerMapping(
FilteringWebHandler webHandler, RouteLocator routeLocator,
GlobalCorsProperties globalCorsProperties, Environment environment) {
return new RoutePredicateHandlerMapping(webHandler, routeLocator,
globalCorsProperties, environment);
}
//接著看RoutePredicateHandlerMapping
public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {
private final FilteringWebHandler webHandler;
private final RouteLocator routeLocator;
private final Integer managmentPort;
public RoutePredicateHandlerMapping(FilteringWebHandler webHandler, RouteLocator routeLocator, GlobalCorsProperties globalCorsProperties, Environment environment) {
this.webHandler = webHandler;
this.routeLocator = routeLocator;
if (environment.containsProperty("management.server.port")) {
managmentPort = new Integer(environment.getProperty("management.server.port"));
} else {
managmentPort = null;
}
//這裡設定了順序,使得閘道器路由的匹配在requestmapping和handlefunction之後
setOrder(1);
setCorsConfigurations(globalCorsProperties.getCorsConfigurations());
}
@Override//方法在原始碼解析(1)中說的AbstractHandlerMapping類中獲取Handler時呼叫
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
// don`t handle requests on the management port if set
if (managmentPort != null && exchange.getRequest().getURI().getPort() == managmentPort.intValue()) {
//不處理特定配置埠的路由,返回empty即不用閘道器路由的handler,AbstractHandlerMapping繼續找下一個
return Mono.empty();
}
exchange.getAttributes().put(GATEWAY_HANDLER_MAPPER_ATTR, getSimpleName());
return lookupRoute(exchange)//查詢路由
// .log("route-predicate-handler-mapping", Level.FINER) //name this
.flatMap((Function<Route, Mono<?>>) r -> {
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
if (logger.isDebugEnabled()) {
logger.debug("Mapping [" + getExchangeDesc(exchange) + "] to " + r);
}
//把Route放到exchange的屬性了,後面有幾個過濾器用到了
exchange.getAttributes().put(GATEWAY_ROUTE_ATTR, r);
//返回構造該類時傳入的FilteringWebHandler,之後的流程就會跟上一篇裡說的走下去了
return Mono.just(webHandler);
}).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
//找不到匹配的路由的話進到這裡,之後基本就是404了
exchange.getAttributes().remove(GATEWAY_PREDICATE_ROUTE_ATTR);
if (logger.isTraceEnabled()) {
logger.trace("No RouteDefinition found for [" + getExchangeDesc(exchange) + "]");
}
})));
}
······
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
//呼叫上面提到的CachingRouteLocator的getRoutes方法獲取所有Routes
return this.routeLocator
.getRoutes()
//individually filter routes so that filterWhen error delaying is not a problem
.concatMap(route -> Mono
.just(route)
.filterWhen(r -> {//過濾出符合的路由
// add the current route we are testing
exchange.getAttributes().put(GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
//呼叫上面說的ServerWebExchangeUtils類裡的toAsyncPredicate方法返回的lambda
//裡面會呼叫Predicate的實現類(都是lambda)的test方法返回true或者false
return r.getPredicate().apply(exchange);
})
//instead of immediately stopping main flux due to error, log and swallow it
.doOnError(e -> logger.error("Error applying predicate for route: "+route.getId(), e))
.onErrorResume(e -> Mono.empty())
)
// .defaultIfEmpty() put a static Route not found
// or .switchIfEmpty()
// .switchIfEmpty(Mono.<Route>empty().log("noroute"))
.next()//過濾出一個後就停止了
//TODO: error handling
.map(route -> {
if (logger.isDebugEnabled()) {
logger.debug("Route matched: " + route.getId());
}
validateRoute(route, exchange);
return route;
});
}
·····
}
說了那麼多終於把閘道器匹配路由的流程說完了,如果上面都看明白的話,動態路由就好辦了
閘道器已經把InMemoryRouteDefinitionRepository註冊成bean(也可以參考這個類自己實現RouteDefinitionRepository介面),我們把它當作個service注入到controller,
前端把RouteDefinition用json的格式post過來,我們呼叫InMemoryRouteDefinitionRepository的save或者delete方法,再用spring的事件觸發RefreshRoutesEvent事件來重新整理路由就行了,等下次請求的時候就可以拿到新的路由配置了
順序是:
1.RoutePredicateHandlerMapping 的lookupRoute方法,由於路由重新整理事件把路由快取清了,所以重新獲取
2.CompositeRouteLocator 的getRoutes方法遍歷所有RouteLocator實現類的getRoutes方法
3.RouteDefinitionRouteLocator 的getRoutes方法裡重新獲取了所有的路由定義,也就把我們剛剛用事件新增的路由也獲取了