Spring cloud gateway入門

微服務Gateway

微服務網關部署在前端Nginx網關和後端微服務之間,Nginx一般充當流量網關,而微服務網關屬於一種業務型 網關,微服務網關層爲後端的微服務羣組提供統一的接入地址,其核心功能是統一做服務路由,在路由基礎上還 可以實現一些橫切性的功能,如:服務權鑑、協議轉換、日誌記錄、白名單控制等業務。

要創建一個Spring cloud gateway微服務網關,在項目中引入spring-cloud-starter-gateway這個起始依賴即可。

Spring Cloud Gateway領域概念

  • 路由(Route)
    • 謂詞(Predicate)
    • 過濾器(Filter)

“路由”是spring-cloud-gateway中的基本處理單元,定義一條路由規則,spring-cloud-gateway會加載 很多路由規則,但對於每個請求來說只會在其中一個路由上生效。

“謂詞”和“Filter”則是從屬於一個路由規則的。

謂詞就是一組斷言,用於對請求內容進行各種判斷匹配,一條路由中可以組合多個謂詞,只有全部謂詞斷言通過後, 路由才生效,多個謂詞條件用 and 操作符組合。

“Filter”則是對謂詞斷言通過後的請求內容以及響應內容進行加工,遵循責任鏈模式,可以用Filter來做如權鑑、 灰度、日誌等通用性功能。

Spring Cloud Gateway工作原理

Spring Cloud Gateway工作原理

謂詞

謂詞就是一個 java.util.function.Predicate 函數,進入網關的請求內容通過一個 ServerWebExchange 對象封裝,將其傳遞到 test 方法進行判斷。

謂詞由 RoutePredicateFactory 來定義,Spring cloud gateway內建了很多謂詞工廠:

  • AfterRoutePredicateFactory 斷言請求發生時間在指定時間之後
  • BeforeRoutePredicateFactory 斷言請求發生時間在指定時間之前
  • BetweenRoutePredicateFactory 斷言請求發生在指定的兩個時間之間
  • PathRoutePredicateFactory 斷言請求路徑符合給出的路徑模式列表
  • MethodRoutePredicateFactory 斷言請求的HTTP動詞在指定的動詞列表中
  • QueryRoutePredicateFactory 斷言指定的查詢參數的值符合給定的模式,模式由Java正則表達式給出
  • HeaderRoutePredicateFactory 斷言指定請求頭的值符合指定的模式,模式由Java正則表達式給出
  • CookieRoutePredicateFactory 斷言請求頭中包含指定的cookie
  • ...

所有內建提供的謂詞工廠可以參考官方文檔

這些謂詞非常靈活,可以自由組合實現各種各樣的判斷效果,可以進入每個謂詞類中的 test 方法查看實現細節。

除了內建的謂詞,用戶也可以基於RoutePredicateFactory進行擴展自定義的謂詞。

Filter

Spring cloud gateway支持兩種filter:

  • GlobalFilter
  • GatewayFilter

GatewayFilter是局部的,從屬於某個路由規則。 GlobalFilter對所有路由規則都會生效,用於處理一些公共性的任務。

同樣的,運行時一個 ServerWebExchange 對象會傳遞到其 filter 方法。

GatewayFilter由GatewayFilterFactory定義,同樣Spring cloud gateway也內建了各種各樣功能的GatewayFilter, 可以參考文檔對這些filter的說明:

當一個請求匹配上某個路由規則後,Spring會把所有全局filter和局部filter都組合到一個FilterChain中,filter執行的 順序由其實現的getOrder方法決定。

配置路由規則

Spring支持通過application.yml配置或者Java代碼配置兩種形式定義路由規則。

#在spring.cloud.gateway.routes 節點下配置即可
#採用yml或者properties配置時,是通過 RouteDefinitionLocator 來定義的
spring:
  cloud:
    gateway:
      routes:
        - id: "路由id"
          uri: lb://order-svc #目標微服務
          predicates: #指定謂詞列表,多個謂詞采用 and 邏輯組合
            - Path="/**" # 匹配任何路徑
            - Header="X-Is-Secure" #是否存在X-Is-Secure請求頭
          filters: #指定gateway filter列表
            - AddRequestHeader=X-Is-Secure,Yes #寫入請求頭 X-Is-Secure=Yes

採用java代碼定義路由規則就更靈活:

//java配置時通過 RouteLocator 來定義路由規則
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("r001", 
                predicate -> predicate
                        .path("/gateway/order/*")
                        .filters(filter -> filter.addRequestHeader("X-Is-Secure", "YES"))
                        .uri("lb://order-svc")
        )
        .route("r002", 
                predicate -> predicate.path("/gateway/product")
                        .and()
                        .header("...")  //and組合
                        .filters(filterSpec -> filterSpec.stripPrefix(1))
                        .uri("lb://product-svc")
        )
        .build();
}

Java配置和properties(yaml)配置對比:

採用配置文件定義路由規則時,多個謂詞斷言採用and方式組合,而採用java代碼配置時,可以靈活選擇 and()or() 或者 negate() 不同的操作符組合。

配置文件定義路由是通過 RouteDefinitionLocator 來解析的,RouteDefinition 類是標準的用於定義 路由規則屬性的組件。而java配置方式則直接通過 RouteLocator 來定義路由規則。

還要注意,無論是java代碼配置,還是properties(yaml)配置,兩種形式定義的規則都是在程序啓動時就被解析並維護起來, 無法在運行時動態修改謂詞條件和過濾器處理邏輯。

路由規則的加載和持久化路由(RouteDefinitionRepository)

路由規則配置數據是由 RouteDefinitionLocator 負責加載的,路由規則存儲維護則由RouteDefinitionRepository 負責,RouteDefinitionRepository接口擴展了RouteDefinitionLocator接口,因此一個RouteDefinitionRepository 的實現者要實現路由規則加載和存儲維護兩個功能。

Spring cloud gateway爲RouteDefinitionRepository提供了2個實現者:

  • InMemoryRouteDefinitionRepository
  • RedisRouteDefinitionRepository

InMemoryRouteDefinitionRepository基於本地內存的策略,它被配置爲默認的路由規則存儲組件,由於是內存維護, 所以是易失的,程序只要重啓路由規則就丟失了。

RedisRouteDefinitionRepository則是基於redis存儲的實現,這個是不易失的,因此可以作爲路由規則的持久化存儲 方式,在多實例gateway節點部署時,可以用RedisRouteDefinitionRepository的方式來實現路由共享。

要開啓基於Redis的路由規則存儲機制,需要啓用屬性 spring.cloud.gateway.redis-route-definition-repository.enabled=true

實現動態路由(RouteDefinitionWriter)

運行時動態修改路由規則就靠RouteDefinitionWriter這個組件,它暴露兩個操作,:

public interface RouteDefinitionWriter {
	Mono<void> save(Mono<routedefinition> route); //修改路由規則
	Mono<void> delete(Mono<string> routeId); //刪除路由規則
}

如果我們把路由規則維護在一個外部配置中心(nacos、zookeeper等),這類配置服務都支持實時推送內容變更,因此 我們可以實現一段監聽配置中心路由規則內容變更程序,它要依賴一個RouteDefinitionWriter對象,當接收到配置中心 的變更推送時,就委託RouteDefinitionWritersave或者delete方法,這樣就實現了路由的動態變更和持久維護。

以Nacos爲例實現一個僞代碼:

//僞代碼,只表達意思
@Component
public class RouteDefinitionDynamicUpdater implements InitializingBean {
    // 這個委託對象是InMemoryRouteDefinitionRepository,或者是RedisRouteDefinitionRepository都可以
    @Autowired
    private RouteDefinitionWriter delegateWriter;
    
    @Override
    public void afterPropertiesSet() {
        ConfigService configService = initNacosConfigService();
        
        //程序第一次啓動是獲取路由規則
        String routeConfigStr = configService.getConfig(dataId, group);
        List<routedefinition> routeDefinitionList = parseJson(routeConfigStr); //假設是json配置
        for (RouteDefinition def : routeDefinitionList) {
          delegateWriter.save(def);
        }
        
        //持續監聽nacos路由規則變更
        configService.addListener(dataId, group, new Listener() {
            public void receiveConfigInfo(String routeConfigStr) {
              List<routedefinition> routeDefinitionList = parseJson(routeConfigStr); //假設是json配置
              for (RouteDefinition def : routeDefinitionList) {
                delegateWriter.save(def);
              }
            }
        });
    }
    
    private ConfigService initNacosConfigService() {
        //...
    }
}

</routedefinition></routedefinition></string></void></routedefinition></void>

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章