歡迎光臨
每天分享高質量文章

閘道器 Spring-Cloud-Gateway 原始碼解析 —— 路由(1.1)之RouteDefinitionLocator一覽

點選上方“芋道原始碼”,選擇“置頂公眾號”

技術文章第一時間送達!

原始碼精品專欄

 

本文主要基於 Spring-Cloud-Gateway 2.0.X M4

  • 1. 概述

  • 2. RouteDefinition

  • 3. PredicateDefinition

  • 4. FilterDefinition

  • 5. RouteDefinitionLocator

  • 6. CompositeRouteDefinitionLocator

  • 666. 彩蛋


1. 概述

本文主要對 路由定義定位器 RouteDefinitionLocator 做整體的認識

在 《Spring-Cloud-Gateway 原始碼解析 —— 閘道器初始化》 中,我們看到路由相關的元件 RouteDefinitionLocator / RouteLocator 的初始化。涉及到的類比較多,我們用下圖重新梳理下 :

  • RouteDefinitionLocator 負責讀取路由配置( org.springframework.cloud.gateway.route.RouteDefinition ) 。從上圖中我們可以看到,RouteDefinitionLocator 介面有四種實現 :

    • PropertiesRouteDefinitionLocator ,從配置檔案( 例如,YML / Properties 等 ) 讀取。在 《Spring-Cloud-Gateway 原始碼解析 —— 路由(1.2)之 PropertiesRouteDefinitionLocator 配置檔案》「2. PropertiesRouteDefinitionLocator」 詳細解析。

    • RouteDefinitionRepository ,從儲存器( 例如,記憶體 / Redis / MySQL 等 )讀取。在 《Spring-Cloud-Gateway 原始碼解析 —— 路由(1.3)之 RouteDefinitionRepository 儲存器》 詳細解析。

    • DiscoveryClientRouteDefinitionLocator ,從註冊中心( 例如,Eureka / Consul / Zookeeper / Etcd 等 )讀取。在 《Spring-Cloud-Gateway 原始碼解析 —— 路由(1.4)之 DiscoveryClientRouteDefinitionLocator 註冊中心》 詳細解析。

    • CompositeRouteDefinitionLocator ,組合多種 RouteDefinitionLocator 的實現,為 RouteDefinitionRouteLocator 提供統一入口。在 本文 詳細解析。

    • 另外,CachingRouteDefinitionLocator 也是 RouteDefinitionLocator 的實現類,已經被 CachingRouteLocator 取代。

  • RouteLocator 可以直接自定義路由( org.springframework.cloud.gateway.route.Route ) ,也可以透過 RouteDefinitionRouteLocator 獲取 RouteDefinition ,並轉換成 Route 。

  • 重要,對於上層呼叫者 RoutePredicateHandlerMapping ,使用的是 RouteLocator 和 Route 。而 RouteDefinitionLocator 和 RouteDefinition 用於透過配置定義路由那麼自定義 RouteLocator 呢?透過程式碼定義路由


推薦 Spring Cloud 書籍

  • 請支援正版。下載盜版,等於主動編寫低階 BUG 。

  • 程式猿DD —— 《Spring Cloud微服務實戰》

  • 周立 —— 《Spring Cloud與Docker微服務架構實戰》

  • 兩書齊買,京東包郵。

推薦 Spring Cloud 影片

  • Java 微服務實踐 – Spring Boot

  • Java 微服務實踐 – Spring Cloud

  • Java 微服務實踐 – Spring Boot / Spring Cloud

2. RouteDefinition

org.springframework.cloud.gateway.route.RouteDefinition ,路由定義。程式碼如下 :

@Validated
public class RouteDefinition {

   @NotEmpty
   private String id = UUID.randomUUID().toString();
   /**
    * 謂語定義陣列
    */

   @NotEmpty
   @Valid
   private List predicates = new ArrayList<>();
   /**
    * 過濾器定義陣列
    */

   @Valid
   private List filters = new ArrayList<>();
   /**
    * 路由向的 URI
    */

   @NotNull
   private URI uri;
   /**
    * 順序
    */

   private int order = 0;
}    
  • id 屬性,ID 編號,唯一

  • predicates 屬性,謂語定義陣列。請求透過 predicates 判斷是否匹配。在 Route 裡,PredicateDefinition 轉換成 Predicate 。

  • filters 屬性,過濾器定義陣列。在 Route 裡,FilterDefinition 轉換成 GatewayFilter 。

  • uri 屬性,路由向的 URI 。

  • order 屬性,順序。當請求匹配到多個路由時,使用順序的。


RouteDefinition 提供 text 字串建立物件,程式碼如下 :

/**
* 根據 text 建立 RouteDefinition
*
* @param text 格式 ${id}=${uri},${predicates[0]},${predicates[1]}...${predicates[n]}
*             例如 route001=http://127.0.0.1,Host=**.addrequestparameter.org,Path=/get
*/

public RouteDefinition(String text) {
   int eqIdx = text.indexOf("=");
   if (eqIdx <= 0) {
       throw new ValidationException("Unable to parse RouteDefinition text '" + text + "'" +
               ", must be of the form name=value");
   }
   // id
   setId(text.substring(0, eqIdx));
   // predicates
   String[] args = tokenizeToStringArray(text.substring(eqIdx+1), ",");
   // uri
   setUri(URI.create(args[0]));

   for (int i=1; i < args.length; i++) {
       this.predicates.add(new PredicateDefinition(args[i]));
   }
}
  • text 引數,格式為 ${id}=${uri},${predicates[0]},${predicates[1]}...${predicates[n]} 。舉個例子, "route001=http://127.0.0.1,Host=**.addrequestparameter.org,Path=/get" 。建立的 RouteDefinition 如下圖 :

  • filters 屬性,需要透過呼叫 RouteDefinition#setFilters(filters) 方法進行設定。

  • order 屬性,需要透過呼叫 RouteDefinition#setOrder(order) 方法進行設定。

  • predicates 屬性,支援解析,但是如果此處單個 PredicateDefinition 的 args[i] 存在逗號, ) ,會被錯誤的分隔,例如說,"Query=foo,bz" 。

3. PredicateDefinition

org.springframework.cloud.gateway.handler.predicate.PredicateDefinition ,謂語定義。請求透過 predicates 判斷是否匹配。程式碼如下 :

@Validated
public class PredicateDefinition {

   /**
    * 謂語定義名字
    */

   @NotNull
   private String name;
   /**
    * 引數陣列
    */

   private Map args = new LinkedHashMap<>();
}
  • name 屬性,謂語定義名字。透過 name 對應到 org.springframework.cloud.gateway.handler.predicate.RoutePredicateFactory 的實現類。例如說,name=Query 對應到 QueryRoutePredicateFactory 。

  • args 屬性,引數陣列。例如,name=Host / args={"_genkey_0" : "iocoder.cn"} ,匹配請求的 hostname 為 iocoder.cn 。


PredicateDefinition 提供 text 字串建立物件,程式碼如下 :

/**
* 根據 text 建立 PredicateDefinition
*
* @param text 格式 ${name}=${args[0]},${args[1]}...${args[n]}
*             例如 Host=iocoder.cn
*/

public PredicateDefinition(String text) {
   int eqIdx = text.indexOf("=");
   if (eqIdx <= 0) {
       throw new ValidationException("Unable to parse PredicateDefinition text '" + text + "'" +
               ", must be of the form name=value");
   }
   // name
   setName(text.substring(0, eqIdx));
   // args
   String[] args = tokenizeToStringArray(text.substring(eqIdx+1), ",");
   for (int i=0; i < args.length; i++) {
       this.args.put(NameUtils.generateName(i), args[i]);
   }
}
  • text 引數,格式為 ${name}=${args[0]},${args[1]}...${args[n]} 。舉個例子, "Host=iocoder.cn" 。建立的 PredicateDefinition 如下圖 :

4. FilterDefinition

FilterDefinition 和 PredicateDefinition 的程式碼實現上基本一致

org.springframework.cloud.gateway.filter.FilterDefinition ,過濾器定義。程式碼如下 :

@Validated
public class FilterDefinition {

   /**
    * 過濾器定義名字
    */

   @NotNull
   private String name;
   /**
    * 引數陣列
    */

   private Map args = new LinkedHashMap<>();
}
  • name 屬性,過濾器定義名字。透過 name 對應到 org.springframework.cloud.gateway.filter.factory.GatewayFilterFactory 的實現類。例如說,name=AddRequestParameter 對應到 AddRequestParameterGatewayFilterFactory 。

  • args 屬性,引數陣列。例如,name=AddRequestParameter / args={"_genkey_0": "foo", "_genkey_1": "bar"} ,新增請求引數 foo 為 bar 。


FilterDefinition 提供 text 字串建立物件,程式碼如下 :

/**
* 根據 text 建立 FilterDefinition
*
* @param text 格式 ${name}=${args[0]},${args[1]}...${args[n]}
*             例如 AddRequestParameter=foo, bar
*/

public FilterDefinition(String text) {
   int eqIdx = text.indexOf("=");
   if (eqIdx <= 0) {
       setName(text);
       return;
   }
   // name
   setName(text.substring(0, eqIdx));
   // args
   String[] args = tokenizeToStringArray(text.substring(eqIdx+1), ",");
   for (int i=0; i < args.length; i++) {
       this.args.put(NameUtils.generateName(i), args[i]);
   }
}
  • text 引數,格式為 ${name}=${args[0]},${args[1]}...${args[n]} 。舉個例子, "AddRequestParameter=foo, bar" 。建立的 FilterDefinition 如下圖 :

5. RouteDefinitionLocator

org.springframework.cloud.gateway.route.RouteDefinitionLocator ,路由定義定位器介面,定義獲得路由定義陣列的方法。程式碼如下 :

public interface RouteDefinitionLocator {

   Flux getRouteDefinitions();
}
  • 對 Reactor Flux 暫時不熟悉的同學,可以閱讀完本文 Google 進行學習。隨著 Spring 5 對響應式程式設計的推廣,厲害如你一定要去掌握。

在上文中,我們也看到了 RouteDefinitionLocator 的多個實現類,類圖如下 :

  • 本文只解析 CompositeRouteDefinitionLocator 的原始碼實現。其他的實現類會在後面文章詳細解析。

6. CompositeRouteDefinitionLocator

org.springframework.cloud.gateway.route.CompositeRouteDefinitionLocator ,組合多種RouteDefinitionLocator 的實現,為 RouteDefinitionRouteLocator 提供統一入口。程式碼如下 :

public class CompositeRouteDefinitionLocator implements RouteDefinitionLocator {

   /**
    * RouteDefinitionLocator 陣列
    */

   private final Flux delegates;

   public CompositeRouteDefinitionLocator(Flux delegates) {
       this.delegates = delegates;
   }

   @Override
   public Flux getRouteDefinitions() {
       return this.delegates.flatMap(RouteDefinitionLocator::getRouteDefinitions);
   }

}
  • #getRouteDefinitions() 方法,提供統一方法,將組合的 delegates 的路由定義全部傳回。

666. 彩蛋

知識星球

RouteDefinition => Route 
PredicateDefinition => Predication 
FilterDefinition => GatewayFilter

等等的轉換,我們在後續路由相關的文章詳細解析。

胖友,分享一波朋友圈可好!

贊(0)

分享創造快樂