Spring Cloud Gateway(五):路由定位器 RouteLocator

本文基于 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 的流程

![image](路由轉換流程圖

// RouteDefinitionRouteLocator.java

@Overridepublic 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 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 主要從后者獲取路由定義信息。

轉載于:https://www.cnblogs.com/liukaifeng/p/10055868.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/248952.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/248952.shtml
英文地址,請注明出處:http://en.pswp.cn/news/248952.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

CommonJS,AMD,CMD區別 - 鄭星陽 - ITeye博客

CommonJS&#xff0c;AMD&#xff0c;CMD區別 博客分類&#xff1a; seajs和requirejs JavaScript zccst轉載 學得比較暈&#xff0c;再次看commonjs&#xff0c;amd, cmd時好像還是沒完全弄清楚&#xff0c;今天再整理一下&#xff1a; commonjs是用在服務器端的&#xff…

739. Daily Temperatures

根據每日 氣溫 列表&#xff0c;請重新生成一個列表&#xff0c;對應位置的輸入是你需要再等待多久溫度才會升高的天數。如果之后都不會升高&#xff0c;請輸入 0 來代替。 例如&#xff0c;給定一個列表 temperatures [73, 74, 75, 71, 69, 72, 76, 73]&#xff0c;你的輸出應…

【NOIP2018】DAY2T2——填數游戲(輪廓線狀壓的dp?搜索打表)

描述 小 D 特別喜歡玩游戲。這一天&#xff0c;他在玩一款填數游戲。 這個填數游戲的棋盤是一個n m的矩形表格。玩家需要在表格的每個格子中填入一個數字&#xff08;數字 0 或者數字 1&#xff09;&#xff0c;填數時需要滿足一些限制。 下面我們來具體描述這些限制。 為了方…

Mysql中遇到的錯誤

Caused by: java.sql.SQLException: Unknown system variable ‘tx_isolation’ 這種錯誤是因為數據庫版本新的但是mysql的jar包是舊的&#xff0c;所以導入最新的mysqljar包 注意實體類和數據庫字段的映射關系&#xff0c;實體類中使用駝峰式的命名規則&#xff0c;大寫的字母…

Express 入門之Router - worldtree_keeper的專欄 - CSDN博客

要了解Router我們需要先知道到Application&#xff0c;首先&#xff0c;每一個express實例本身內部就內建了router&#xff0c;所以我們先從簡單的下手&#xff0c;先使用application&#xff1b;另外這里我們只選擇get方法&#xff0c;作為我們Router.Method, 之所以使用get是…

rest測試定義

1.為什么要做接口測試&#xff1a; 1.因為很多系統關聯都是基于接口實現的&#xff0c;接口測試可以將系統復雜的系統關聯進行簡化 2.接口工程比較單一&#xff0c;能夠比較好的進行測試覆蓋&#xff0c;也相對容易實現自動化持續集成 3.接口相對于界面功能 &#xff0c;會更底…

團隊開發進度報告9

&#xff08;1&#xff09;站立會議 &#xff08;2&#xff09;任務面板 &#xff08;3&#xff09;具體內容 昨天&#xff1a;完成了界面控件按鈕的設置問題&#xff1a;PHP數據處理&#xff0c;如何實現在線數據交互問題今天&#xff1a;hbuilder后臺環境搭建 轉載于:https:/…

nodejs+express整合kindEditor實現圖片上傳 - 木子豐咪咕晶 - 開源中國

kindEditor官網上中提供了ASP,ASP.NET,JSP相關的整合應用,http://kindeditor.net/docs/upload.html可以參照實現nodejs的整合,發現實用nodejs更簡單 環境: unbuntu 14.10 nodejs 0.10.35 express 4.11.2 formidable 1.0.16 kindEditor 4.1.10 webStorm 8 1.通過IDE或終端創建…

基于springboot多模塊項目使用maven命令打成war包放到服務器上運行的問題

首先&#xff0c;大家看到這個問題&#xff0c;可能并不陌生&#xff0c;而且腦子里第一映像就是使用mava中的clear package 或者 clear install進行打包&#xff0c;然后在項目中的target文件夾下面找到xxx.war&#xff0c;將這個war包放到外置的tomcat服務器下的webapps下面&…

Kafka學習筆記(3)----Kafka的數據復制(Replica)與Failover

1. CAP理論 1.1 Cosistency(一致性) 通過某個節點的寫操作結果對后面通過其他節點的讀操作可見。 如果更新數據后&#xff0c;并發訪問的情況下可立即感知該更新&#xff0c;稱為強一致性 如果允許之后部分或全部感知不到該更新&#xff0c;稱為弱一致性。 若在之后的一段時間&…

H5頁面隨機數字鍵盤支付頁面

H5頁面隨機數字鍵盤支付頁面 有個H5支付的業務需要隨機數字的鍵盤 參考了下文&#xff1a;https://blog.csdn.net/Mr_Smile2014/article/details/52473351 做了一些小修改&#xff1a; 在原有的基礎上&#xff0c;增加了一些按鍵反饋的效果。 每個按鍵加上邊框。 最終效果&…

expressjs路由和Nodejs服務器端發送REST請求 - - ITeye博客

Nodejs創建自己的server后&#xff0c;我們如果需要從客戶端利用ajax調用別的服務器端的數據API的接口&#xff0c;這時候出現了ajax跨域問題。 一種是利用在客戶端解決跨域問題 這種方案大家可以去網上查查 另一種方案是在服務器端去請求別的服務器&#xff0c;然后將數據再…

Jmeter操作mysql數據庫測試

1. 選中線程組鼠標點擊右鍵添加-->配置元件-->JDBC Connection Configuration&#xff1b; 2. DataBase Connection Configuration配置 Variable Name&#xff1a;配置元件的的所有配置所保存的變量&#xff0c;自定義變量名稱(不能使用mysql作為變量名&#xff0c;多個…

axios發送自定義請求頭的跨域解決

前端發送來的axios請求信息 this.$axios.request({ url:http://127.0.0.1:8001/pay/shoppingcar/, method:post, headers:{ authenticate:a073b3dabbb140e8b9d28debb6a356a1 # 自定義的請求頭部信息鍵值對, }, # 接上,這種key也算是一種請求頭,需要加入django中間件內…

前端“智能”靜態資源管理 - Onebox - 博客園

前端“智能”靜態資源管理 模塊化/組件化開發&#xff0c;僅僅描述了一種開發理念&#xff0c;也可以認為是一種開發規范&#xff0c;倘若你認可這規范&#xff0c;對它的分治策略產生了共鳴&#xff0c;那我們就可以繼續聊聊它的具體實現了。 很明顯&#xff0c;模塊化/組件化…

【轉】幾張圖看懂列式存儲

幾張圖看懂列式存儲 轉載于:https://www.cnblogs.com/apeway/p/10870211.html

hive -e和hive -f的區別(轉)

大家都知道&#xff0c;hive -f 后面指定的是一個文件&#xff0c;然后文件里面直接寫sql&#xff0c;就可以運行hive的sql&#xff0c;hive -e 后面是直接用雙引號拼接hivesql&#xff0c;然后就可以執行命令。 但是&#xff0c;有這么一個東西&#xff0c;我的sql當中有一個s…

我們是如何做好前端工程化和靜態資源管理 - 無雄 - 博客園

我們是如何做好前端工程化和靜態資源管理 隨著互聯網的發展&#xff0c;我們的業務也日益變得更加復雜且多樣化起來&#xff0c;前端工程師也不再只是做簡單的頁面開發這么簡單&#xff0c;我們需要面對的十分復雜的系統性問題&#xff0c;例如&#xff0c;業務愈來愈復雜&…

Mybatis-plus之RowBounds實現分頁查詢

物理分頁和邏輯分頁 物理分頁&#xff1a;直接從數據庫中拿出我們需要的數據&#xff0c;例如在Mysql中使用limit。 邏輯分頁&#xff1a;從數據庫中拿出所有符合要求的數據&#xff0c;然后再從這些數據中拿到我們需要的分頁數據。 優缺點 物理分頁每次都要訪問數據庫&#xf…

常見的6種JavaScript設計模式

常見的6種JavaScript設計模式 構造函數模式 /*** 構造一個動物的函數 */ function Animal(name, color){this.name name;this.color color;this.getName function(){return this.name;} } // 實例一個對象 var cat new Animal(貓, 白色); console.log( cat.getName() );工…