本文基于 spring cloud gateway 2.0.1
1、簡介
直接 獲取 路 由 的 方法 是 通過 RouteLocator 接口 獲取。 同樣, 該 頂 級 接口 有多 個 實現 類,
RouteLocator 路由定位器,顧名思義就是用來獲取路由的方法。該路由定位器為頂級接口有多個實現類,如類圖所示,本節會對其實現類一一進行介紹。
通過類圖可知,路由定位器接口有三種實現方法:
- RouteDefinitionRouteLocator 基于路由定義的定位器
- CachingRouteLocator 基于緩存的路由定位器
- CompositeRouteLocator 基于組合方式的路由定位器
2、RouteLocator 路由定位器
與上節學習的路由定義定位器接口類似,RouteLocator 路由定位器只有一個 getRoutes 方法,用來獲取路由信息。
public interface RouteLocator {//獲取路由對象Flux<Route> getRoutes();
}
2.1、Route 路由對象
Route 路由定義了路由斷言、過濾器、路由地址及路由優先級等信息。當請求到達時,在轉發到代理服務之前,會依次經過路由斷言匹配路由 和 網關過濾器處理。
public class Route implements Ordered {//路由 Id private final String id; //路由地址 private final URI uri; //路由的優先級 private final int order; //路由斷言,判斷請求路徑是否匹配private final AsyncPredicate<ServerWebExchange> predicate;//網關過濾器private final List<GatewayFilter> gatewayFilters;-----------------------省略-------------------------
}
3、 RouteDefinitionRouteLocator 基于路由定義的定位器
3.1、初始化
RouteDefinitionRouteLocator 構造函數有多個參數:路由定義定位器、路由斷言工廠、網關過濾器及網關配置對象。 根據傳入的參數,設置 routeDefinitionLocator 和 網關配置,并初始化路由斷言 和 網關過濾器。 RouteDefinitionRouteLocator 的實現方式是基于路由定義來獲取路由,它實現了 RouteLocator 接口,用來獲取路由信息。
public class RouteDefinitionRouteLocator implements RouteLocator, BeanFactoryAware, ApplicationEventPublisherAware {protected final Log logger = LogFactory.getLog(getClass());private final RouteDefinitionLocator routeDefinitionLocator;private final Map<String, RoutePredicateFactory> predicates = new LinkedHashMap<>();private final Map<String, GatewayFilterFactory> gatewayFilterFactories = new HashMap<>();private final GatewayProperties gatewayProperties;private final SpelExpressionParser parser = new SpelExpressionParser();private BeanFactory beanFactory;private ApplicationEventPublisher publisher;public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator,List<RoutePredicateFactory> predicates,List<GatewayFilterFactory> gatewayFilterFactories,GatewayProperties gatewayProperties) {//設置路由定義定位器this.routeDefinitionLocator = routeDefinitionLocator;//初始化路由斷言工廠initFactories(predicates);//初始化網關過濾器gatewayFilterFactories.forEach(factory -> this.gatewayFilterFactories.put(factory.name(), factory));this.gatewayProperties = gatewayProperties;}@Autowiredprivate Validator validator;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {this.beanFactory = beanFactory;}@Overridepublic void setApplicationEventPublisher(ApplicationEventPublisher publisher) {this.publisher = publisher;}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 (logger.isInfoEnabled()) {logger.info("Loaded RoutePredicateFactory [" + key + "]");}});}--------------------------------省略---------------------------------
}
- 此種方式的路由獲取是通過 RouteDefinitionRouteLocator 獲取 RouteDefinition 并將路由定義轉換成路由對象
- 這里的routeDefinitionLocator是CompositeRouteDefinitionLocator,它組合了InMemoryRouteDefinitionRepository、PropertiesRouteDefinitionLocator、DiscoveryClientRouteDefinitionLocator三個RouteDefinitionLocator。
- PropertiesRouteDefinitionLocator是直接使用GatewayProperties的getRoutes()獲取,其是通過spring.cloud.gateway.routes配置得來。
3.2、RouteDefinition 轉換成 Route 的流程
 {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 loggingif (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();}
-
getRoutes() :根據傳入的 RouteDefinitionLocator 獲取路由定義對象,使用map方法將每個 RouteDefinition 轉換為 Route。
-
RouteDefinitionLocator#convertToRoute :是具體的轉換方法,轉換過程中涉及到路由斷言 和 網關過濾器的處理,最后構建為Route 對象。
-
此處網關過濾器處理包括兩種,一種是默認過濾器,作用于所有路由;一種是指定路由的自定義過濾器。首先獲取默認過濾器,根據過濾器名稱獲取對應的過濾器,最終轉換成有優先級的OrderedGatewayFilter。
3.2.1、convertToRoute##combinePredicates
combinePredicates主要是對找出來的predicate進行and操作
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;}@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);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));}return factory.applyAsync(config);}
3.2.2、convertToRoute##getFilters
getFilters 主要是利用loadGatewayFilters獲取filter,使用AnnotationAwareOrderComparator進行排序
loadGatewayFilters利用工廠方法,使用GatewayFilterFactory根據config 獲取具體的GatewayFilter實例
@SuppressWarnings("unchecked")private List<GatewayFilter> loadGatewayFilters(String id, List<FilterDefinition> filterDefinitions) {List<GatewayFilter> filters = filterDefinitions.stream().map(definition -> {GatewayFilterFactory factory = this.gatewayFilterFactories.get(definition.getName());if (factory == null) {throw new IllegalArgumentException("Unable to find GatewayFilterFactory with name " + definition.getName());}Map<String, String> args = definition.getArgs();if (logger.isDebugEnabled()) {logger.debug("RouteDefinition " + id + " applying filter " + args + " to " + definition.getName());}Map<String, Object> properties = factory.shortcutType().normalize(args, factory, this.parser, this.beanFactory);Object configuration = factory.newConfig();ConfigurationUtils.bind(configuration, properties,factory.shortcutFieldPrefix(), definition.getName(), validator);GatewayFilter gatewayFilter = factory.apply(configuration);if (this.publisher != null) {this.publisher.publishEvent(new FilterArgsEvent(this, id, properties));}return gatewayFilter;}).collect(Collectors.toList());ArrayList<GatewayFilter> ordered = new ArrayList<>(filters.size());for (int i = 0; i < filters.size(); i++) {GatewayFilter gatewayFilter = filters.get(i);if (gatewayFilter instanceof Ordered) {ordered.add(gatewayFilter);}else {ordered.add(new OrderedGatewayFilter(gatewayFilter, i + 1));}}return ordered;}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("defaultFilters",this.gatewayProperties.getDefaultFilters()));}if (!routeDefinition.getFilters().isEmpty()) {filters.addAll(loadGatewayFilters(routeDefinition.getId(), routeDefinition.getFilters()));}AnnotationAwareOrderComparator.sort(filters);return filters;}
4、 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).onCacheMissResume(() -> this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE));}@Overridepublic Flux<Route> getRoutes() {return this.routes;}/*** Clears the routes cache* @return routes flux*/public Flux<Route> refresh() {this.cache.clear();return this.routes;}@EventListener(RefreshRoutesEvent.class)/* for testing */ void handleRefresh() {refresh();}
}
基于緩存的路由定位器比較簡單和緩存路由定義定位器比較類似,只需要調用 RouteLocator# getRoutes 即可獲取路由。
根據傳入的路由定位器獲取路由信息并存儲到緩存中。通過監聽 RefreshRoutesEvent 事件刷新緩存的路由信息。
5、 CompositeRouteLocator 基于組合方式的路由定位器
public class CompositeRouteLocator implements RouteLocator {private final Flux<RouteLocator> delegates;public CompositeRouteLocator(Flux<RouteLocator> delegates) {this.delegates = delegates;}@Overridepublic Flux<Route> getRoutes() {return this.delegates.flatMap(RouteLocator::getRoutes);}
}
組合方式的路由定位器,將實現 RouteLocator 接口的路由定位器組合在一起,提供獲取路由的統一入口。
6、小結
RouteLocator 接口用于獲取路由信息,其有三個實現類
- RouteDefinitionRouteLocator
- CompositeRouteLocator
- CachingRouteLocator
最終使用的是CachingRouteLocator,它包裝了CompositeRouteLocator,而CompositeRouteLocator則組合了RouteDefinitionRouteLocator。
RouteDefinitionRouteLocator 與 RouteDefinitionLocator比較容易混淆,前者是一個RouteLocator(路由定位器),后者是一個RouteDefinitionLocator(路由定義定位器),前者的 RouteDefinitionRouteLocator 主要從后者獲取路由定義信息。