目錄1.1SpringCloud Gateway 簡介1.2 SpringCloud Gateway 特徵1.3 SpringCloud Gateway和架構2 路由配置方式3 路由 匹配規則4 高階配置5 整合Nacos6 整合Swagger聚合微服務系統API文件7 Gatway 閘道器的過濾器開發8 整合Sentinel完成流控和降級1.1 SpringCloud Gateway 簡介
SpringCloud Gateway 是 Spring Cloud 的一個全新專案,該專案是基於 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技術開發的閘道器,它旨在為微服務架構提供一種簡單有效的統一的 API 路由管理方式。
SpringCloud Gateway 作為 Spring Cloud 生態系統中的閘道器,目標是替代 Zuul,在Spring Cloud 2.0以上版本中,沒有對新版本的Zuul 2.0以上最新高效能版本進行整合,仍然還是使用的Zuul 2.0之前的非Reactor模式的老版本。而為了提升閘道器的效能,SpringCloud Gateway是基於WebFlux框架實現的,而WebFlux框架底層則使用了高效能的Reactor模式通訊框架Netty。
Spring Cloud Gateway 的目標,不僅提供統一的路由方式,並且基於 Filter 鏈的方式提供了閘道器基本的功能,例如:安全,監控/指標,和限流。
提前宣告:Spring Cloud Gateway 底層使用了高效能的通訊框架Netty。
1.2 SpringCloud Gateway 特徵
SpringCloud官方,對SpringCloud Gateway 特徵介紹如下:
(1)基於 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
(2)整合 Hystrix 斷路器
(3)整合 Spring Cloud DiscoveryClient
(4)Predicates 和 Filters 作用於特定路由,易於編寫的 Predicates 和 Filters
(5)具備一些閘道器的高階功能:動態路由、限流、路徑重寫
從以上的特徵來說,和Zuul的特徵差別不大。SpringCloud Gateway和Zuul主要的區別,還是在底層的通訊框架上。
簡單說明一下上文中的三個術語:
(1)Filter(過濾器):
和Zuul的過濾器在概念上類似,可以使用它攔截和修改請求,並且對上游的響應,進行二次處理。過濾器為org.springframework.cloud.gateway.filter.GatewayFilter類的例項。
(2)Route(路由):
閘道器配置的基本組成模組,和Zuul的路由配置模組類似。一個Route模組由一個 ID,一個目標 URI,一組斷言和一組過濾器定義。如果斷言為真,則路由匹配,目標URI會被訪問。
(3)Predicate(斷言):
這是一個 Java 8 的 Predicate,可以使用它來匹配來自 HTTP 請求的任何內容,例如 headers 或引數。斷言的輸入型別是一個 ServerWebExchange。
1.3 SpringCloud Gateway和架構
Spring在2017年下半年迎來了Webflux,Webflux的出現填補了Spring在響應式程式設計上的空白,Webflux的響應式程式設計不僅僅是程式設計風格的改變,而且對於一系列的著名框架,都提供了響應式訪問的開發包,比如Netty、Redis等等。
SpringCloud Gateway 使用的Webflux中的reactor-netty響應式程式設計元件,底層使用了Netty通訊框架。
1.3.1 SpringCloud Zuul的IO模型
Springcloud中所整合的Zuul版本,採用的是Tomcat容器,使用的是傳統的Servlet IO處理模型。
大家知道,servlet由servlet container進行生命週期管理。container啟動時構造servlet物件並呼叫servlet init()進行初始化;container關閉時呼叫servlet destory()銷燬servlet;container執行時接受請求,併為每個請求分配一個執行緒(一般從執行緒池中獲取空閒執行緒)然後呼叫service()。
弊端:servlet是一個簡單的網路IO模型,當請求進入servlet container時,servlet container就會為其繫結一個執行緒,在併發不高的場景下這種模型是適用的,但是一旦併發上升,執行緒數量就會上漲,而執行緒資源代價是昂貴的(上線文切換,記憶體消耗大)嚴重影響請求的處理時間。在一些簡單的業務場景下,不希望為每個request分配一個執行緒,只需要1個或幾個執行緒就能應對極大併發的請求,這種業務場景下servlet模型沒有優勢。
所以Springcloud Zuul 是基於servlet之上的一個阻塞式處理模型,即spring實現了處理所有request請求的一個servlet(DispatcherServlet),並由該servlet阻塞式處理處理。所以Springcloud Zuul無法擺脫servlet模型的弊端。雖然Zuul 2.0開始,使用了Netty,並且已經有了大規模Zuul 2.0叢集部署的成熟案例,但是,Springcloud官方已經沒有整合改版本的計劃了。
1.3.2Webflux 伺服器
Webflux模式替換了舊的Servlet執行緒模型。用少量的執行緒處理request和response io操作,這些執行緒稱為Loop執行緒,而業務交給響應式程式設計框架處理,響應式程式設計是非常靈活的,使用者可以將業務中阻塞的操作提交到響應式框架的work執行緒中執行,而不阻塞的操作依然可以在Loop執行緒中進行處理,大大提高了Loop執行緒的利用率。官方結構圖:
Webflux雖然可以相容多個底層的通訊框架,但是一般情況下,底層使用的還是Netty,畢竟,Netty是目前業界認可的最高效能的通訊框架。而Webflux的Loop執行緒,正好就是著名的Reactor 模式IO處理模型的Reactor執行緒,如果使用的是高效能的通訊框架Netty,這就是Netty的EventLoop執行緒。
關於Reactor執行緒模型,和Netty通訊框架的知識,是Java程式設計師的重要、必備的內功,箇中的原理,具體請參見尼恩編著的《Netty、Zookeeper、Redis高併發實戰》一書,這裡不做過多的贅述。
1.3.3Spring Cloud Gateway的處理流程
客戶端向 Spring Cloud Gateway 發出請求。然後在 Gateway Handler Mapping 中找到與請求相匹配的路由,將其傳送到 Gateway Web Handler。Handler 再透過指定的過濾器鏈來將請求傳送到我們實際的服務執行業務邏輯,然後返回。過濾器之間用虛線分開是因為過濾器可能會在傳送代理請求之前(“pre”)或之後(“post”)執行業務邏輯。
2 路由配置方式2.1 基礎URI路由配置方式
如果請求的目標地址,是單個的URI資源路徑,配置檔案示例如下:
server: port: 8080spring: application: name: api-gateway cloud: gateway: routes: -id: url-proxy-1 uri: https://blog.csdn.net predicates: -Path=/csdn
各欄位含義如下:
id:我們自定義的路由 ID,保持唯一
uri:目標服務地址
predicates:路由條件,Predicate 接受一個輸入引數,返回一個布林值結果。該介面包含多種預設方法來將 Predicate 組合成其他複雜的邏輯(比如:與,或,非)。
上面這段配置的意思是,配置了一個 id 為 url-proxy-1的URI代理規則,路由的規則為:
當訪問地址http://localhost:8080/csdn/1.jsp時,
會路由到上游地址https://blog.csdn.net/1.jsp。
package com.springcloud.gateway; import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.gateway.route.RouteLocator;import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;import org.springframework.context.annotation.Bean; @SpringBootApplicationpublic class GatewayApplication { public static void main(String[] args) { SpringApplication.run(GatewayApplication.class, args); } @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("path_route", r -> r.path("/csdn") .uri("https://blog.csdn.net")) .build(); } }
我們在yaml配置檔案中登出掉相關路由的配置,重啟服務,訪問連結:http://localhost:8080/ csdn, 可以看到和上面一樣的頁面,證明我們測試成功。
上面兩個示例中 uri 都是指向了我的CSDN部落格,在實際專案使用中可以將 uri 指向對外提供服務的專案地址,統一對外輸出介面。
2.3 和註冊中心相結合的路由配置方式
在uri的schema協議部分為自定義的lb:型別,表示從微服務註冊中心(如Eureka)訂閱服務,並且進行服務的路由。
一個典型的示例如下:
server: port: 8084spring: cloud: gateway: routes: -id: seckill-provider-route uri: lb://seckill-provider predicates: - Path=/seckill-provider/** -id: message-provider-route uri: lb://message-provider predicates: -Path=/message-provider/**application: name: cloud-gatewayeureka: instance: prefer-ip-address: true client: service-url: defaultZone: http://localhost:8888/eureka/
註冊中心相結合的路由配置方式,與單個URI的路由配置,區別其實很小,僅僅在於URI的schema協議不同。單個URI的地址的schema協議,一般為http或者https協議。
3 路由 匹配規則
Spring Cloud Gateway 的功能很強大,我們僅僅透過 Predicates 的設計就可以看出來,前面我們只是使用了 predicates 進行了簡單的條件匹配,其實 Spring Cloud Gataway 幫我們內建了很多 Predicates 功能。
Spring Cloud Gateway 是透過 Spring WebFlux 的 HandlerMapping 做為底層支援來匹配到轉發路由,Spring Cloud Gateway 內建了很多 Predicates 工廠,這些 Predicates 工廠透過不同的 HTTP 請求引數來匹配,多個 Predicates 工廠可以組合使用。
Route(路由) | ||
Predicate(謂語、斷言) | ||
Filter(過濾器) |
其中Route和Predicate必須同時申明
例子:
//透過配置檔案配置spring: cloud: gateway: routes: - id: gate_route uri: http://localhost:9023 predicates: ## 當請求的路徑為gate、rule開頭的時,轉發到http://localhost:9023伺服器上 - Path=/gate/**,/rule/** ### 請求路徑前加上/app filters: - PrefixPath=/app
3.1 Predicate 斷言條件(轉發規則)介紹
Predicate 來源於 Java 8,是 Java 8 中引入的一個函式,Predicate 接受一個輸入引數,返回一個布林值結果。該介面包含多種預設方法來將 Predicate 組合成其他複雜的邏輯(比如:與,或,非)。可以用於介面請求引數校驗、判斷新老資料是否有變化需要進行更新操作。
在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性實現了各種路由匹配規則,有透過 Header、請求引數等不同的條件來進行作為條件匹配到對應的路由。網上有一張圖總結了 Spring Cloud 內建的幾種 Predicate 的實現。
說白了 Predicate 就是為了實現一組匹配規則,方便讓請求過來找到對應的 Route 進行處理,接下來我們接下 Spring Cloud GateWay 內建幾種 Predicate 的使用。
轉發規則(predicates),假設 轉發uri都設定為http://localhost:9023
規則 |
例項 |
說明 |
Path |
- Path=/gate/,/rule/ | |
Before |
- Before=2017-01-20T17:42:47.789-07:00[America/Denver] | |
After |
- After=2017-01-20T17:42:47.789-07:00[America/Denver] | |
Between |
- Between=2017-01-20T17:42:47.789-07:00[America/Denver],2017-01-21T17:42:47.789-07:00[America/Denver] | |
Cookie |
- Cookie=chocolate, ch.p | |
Header |
- Header=X-Request-Id, \d+ |
攜帶引數X-Request-Id或者滿足\d+的請求頭才會匹配 |
Host |
- Host=www.hd123.com | |
Method | - Method=GET |
3.2 過濾器規則(Filter)過濾器規則(Filter)
過濾規則 |
例項 |
說明 |
PrefixPath |
- PrefixPath=/app |
在請求路徑前加上app |
RewritePath |
- RewritePath=/test, /app/test | |
SetPath |
SetPath=/app/{path} | |
RedirectTo | 重定向 | |
RemoveRequestHeader |
去掉某個請求頭資訊 |
注:當配置多個filter時,優先定義的會被呼叫,剩餘的filter將不會生效
3.3 透過程式碼進行配置
@Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("path_route", r -> r.path("/get") .uri("http://httpbin.org")) .route("host_route", r -> r.host("*.myhost.org") .uri("http://httpbin.org")) .route("rewrite_route", r -> r.host("*.rewrite.org") .filters(f -> f.rewritePath("/foo/(?<segment>.*)", "/${segment}")) .uri("http://httpbin.org")) .route("hystrix_route", r -> r.host("*.hystrix.org") .filters(f -> f.hystrix(c -> c.setName("slowcmd"))) .uri("http://httpbin.org")) .route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org") .filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback"))) .uri("http://httpbin.org")) .route("limit_route", r -> r .host("*.limited.org").and().path("/anything/**") .filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter()))) .uri("http://httpbin.org")) .build(); }
3.2實現熔斷降級
@Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route("path_route", r -> r.path("/get") .uri("http://httpbin.org")) .route("host_route", r -> r.host("*.myhost.org") .uri("http://httpbin.org")) .route("rewrite_route", r -> r.host("*.rewrite.org") .filters(f -> f.rewritePath("/foo/(?<segment>.*)", "/${segment}")) .uri("http://httpbin.org")) .route("hystrix_route", r -> r.host("*.hystrix.org") .filters(f -> f.hystrix(c -> c.setName("slowcmd"))) .uri("http://httpbin.org")) .route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org") .filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback"))) .uri("http://httpbin.org")) .route("limit_route", r -> r .host("*.limited.org").and().path("/anything/**") .filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter()))) .uri("http://httpbin.org")) .build(); }
為什麼要實現熔斷降級?
在分散式系統中,閘道器作為流量的入口,因此會有大量的請求進入閘道器,向其他服務發起呼叫,其他服務不可避免的會出現呼叫失敗(超時、異常),失敗時不能讓請求堆積在閘道器上,需要快速失敗並返回給客戶端,想要實現這個要求,就必須在閘道器上做熔斷、降級操作。
為什麼在閘道器上請求失敗需要快速返回給客戶端?
因為當一個客戶端請求發生故障的時候,這個請求會一直堆積在閘道器上,當然只有一個這種請求,閘道器肯定沒有問題(如果一個請求就能造成整個系統癱瘓,那這個系統可以下架了),但是閘道器上堆積多了就會給閘道器乃至整個服務都造成巨大的壓力,甚至整個服務宕掉。因此要對一些服務和頁面進行有策略的降級,以此緩解伺服器資源的的壓力,以保證核心業務的正常執行,同時也保持了客戶和大部分客戶的得到正確的相應,所以需要閘道器上請求失敗需要快速返回給客戶端。
4 高階配置4.1 分散式限流
從某種意義上講,令牌桶演算法是對漏桶演算法的一種改進,桶演算法能夠限制請求呼叫的速率,而令牌桶演算法能夠在限制呼叫的平均速率的同時還允許一定程度的突發呼叫。在令牌桶演算法中,存在一個桶,用來存放固定數量的令牌。演算法中存在一種機制,以一定的速率往桶中放令牌。每次請求呼叫需要先獲取令牌,只有拿到令牌,才有機會繼續執行,否則選擇選擇等待可用的令牌、或者直接拒絕。放令牌這個動作是持續不斷的進行,如果桶中令牌數達到上限,就丟棄令牌,所以就存在這種情況,桶中一直有大量的可用令牌,這時進來的請求就可以直接拿到令牌執行,比如設定qps為100,那麼限流器初始化完成一秒後,桶中就已經有100個令牌了,這時服務還沒完全啟動好,等啟動完成對外提供服務時,該限流器可以抵擋瞬時的100個請求。所以,只有桶中沒有令牌時,請求才會進行等待,最後相當於以一定的速率執行。
4.2 健康檢查配置4.3 統一配置跨域請求:spring: cloud: gateway: globalcors: cors-configurations: '[/**]': allowed-origins: "*" allowed-headers: "*" allow-credentials: true allowed-methods: - GET - POST - DELETE - PUT - OPTION
5 整合Nacosmaven依賴服務發現配置:從Nacos獲取微服務提供者清單
server: port: 8087spring: application: name: nacos_gateway cloud: nacos: discovery: server-addr: 127.0.0.1:8848 gateway: discovery: locator: enabled: true #表明gateway開啟服務註冊和發現的功能,並且spring cloud gateway自動根據服務發現為每一個服務建立了一個router,這個router將以服務名開頭的請求路徑轉發到對應的服務 lower-case-service-id: true #是將請求路徑上的服務名配置為小寫(因為服務註冊的時候,向註冊中心註冊時將服務名轉成大寫的了 routes: -id: apiuser # uri: lb://nacos-consumer-user predicates: # http://localhost:6601/user/user/users/2, 必須加上StripPrefix=1,否則訪問服務時會帶上user - Path=/user/** # 轉發該路徑 #以下是配置例子 # - id: 163 #閘道器路由到網易官網 # uri: http://www.163.com/ # predicates: - Path=/163/** # - id: ORDER-SERVICE #閘道器路由到訂單服務order-service # uri: lb://ORDER-SERVICE # predicates: # - Path=/ORDER-SERVICE/** # - id: USER-SERVICE #閘道器路由到使用者服務user-service # uri: lb://USER-SERVICE # predicates: # - Pach=/USER-SERVICE/**
nacos實現動態配置6 整合Swagger聚合微服務系統API文件7 Gatway 閘道器的過濾器開發7.1 過濾器的執行次序
spring: cloud: gateway: globalcors: cors-configurations: '[/**]': allowed-origins: "*" allowed-headers: "*" allow-credentials: true allowed-methods: - GET - POST - DELETE - PUT - OPTION
5 整合Nacosmaven依賴服務發現配置:從Nacos獲取微服務提供者清單
server: port: 8087spring: application: name: nacos_gateway cloud: nacos: discovery: server-addr: 127.0.0.1:8848 gateway: discovery: locator: enabled: true #表明gateway開啟服務註冊和發現的功能,並且spring cloud gateway自動根據服務發現為每一個服務建立了一個router,這個router將以服務名開頭的請求路徑轉發到對應的服務 lower-case-service-id: true #是將請求路徑上的服務名配置為小寫(因為服務註冊的時候,向註冊中心註冊時將服務名轉成大寫的了 routes: -id: apiuser # uri: lb://nacos-consumer-user predicates: # http://localhost:6601/user/user/users/2, 必須加上StripPrefix=1,否則訪問服務時會帶上user - Path=/user/** # 轉發該路徑 #以下是配置例子 # - id: 163 #閘道器路由到網易官網 # uri: http://www.163.com/ # predicates: - Path=/163/** # - id: ORDER-SERVICE #閘道器路由到訂單服務order-service # uri: lb://ORDER-SERVICE # predicates: # - Path=/ORDER-SERVICE/** # - id: USER-SERVICE #閘道器路由到使用者服務user-service # uri: lb://USER-SERVICE # predicates: # - Pach=/USER-SERVICE/**
nacos實現動態配置6 整合Swagger聚合微服務系統API文件7 Gatway 閘道器的過濾器開發7.1 過濾器的執行次序
服務發現配置:從Nacos獲取微服務提供者清單
server: port: 8087spring: application: name: nacos_gateway cloud: nacos: discovery: server-addr: 127.0.0.1:8848 gateway: discovery: locator: enabled: true #表明gateway開啟服務註冊和發現的功能,並且spring cloud gateway自動根據服務發現為每一個服務建立了一個router,這個router將以服務名開頭的請求路徑轉發到對應的服務 lower-case-service-id: true #是將請求路徑上的服務名配置為小寫(因為服務註冊的時候,向註冊中心註冊時將服務名轉成大寫的了 routes: -id: apiuser # uri: lb://nacos-consumer-user predicates: # http://localhost:6601/user/user/users/2, 必須加上StripPrefix=1,否則訪問服務時會帶上user - Path=/user/** # 轉發該路徑 #以下是配置例子 # - id: 163 #閘道器路由到網易官網 # uri: http://www.163.com/ # predicates: - Path=/163/** # - id: ORDER-SERVICE #閘道器路由到訂單服務order-service # uri: lb://ORDER-SERVICE # predicates: # - Path=/ORDER-SERVICE/** # - id: USER-SERVICE #閘道器路由到使用者服務user-service # uri: lb://USER-SERVICE # predicates: # - Pach=/USER-SERVICE/**
nacos實現動態配置6 整合Swagger聚合微服務系統API文件7 Gatway 閘道器的過濾器開發7.1 過濾器的執行次序
server: port: 8087spring: application: name: nacos_gateway cloud: nacos: discovery: server-addr: 127.0.0.1:8848 gateway: discovery: locator: enabled: true #表明gateway開啟服務註冊和發現的功能,並且spring cloud gateway自動根據服務發現為每一個服務建立了一個router,這個router將以服務名開頭的請求路徑轉發到對應的服務 lower-case-service-id: true #是將請求路徑上的服務名配置為小寫(因為服務註冊的時候,向註冊中心註冊時將服務名轉成大寫的了 routes: -id: apiuser # uri: lb://nacos-consumer-user predicates: # http://localhost:6601/user/user/users/2, 必須加上StripPrefix=1,否則訪問服務時會帶上user - Path=/user/** # 轉發該路徑 #以下是配置例子 # - id: 163 #閘道器路由到網易官網 # uri: http://www.163.com/ # predicates: - Path=/163/** # - id: ORDER-SERVICE #閘道器路由到訂單服務order-service # uri: lb://ORDER-SERVICE # predicates: # - Path=/ORDER-SERVICE/** # - id: USER-SERVICE #閘道器路由到使用者服務user-service # uri: lb://USER-SERVICE # predicates: # - Pach=/USER-SERVICE/**
6 整合Swagger聚合微服務系統API文件7 Gatway 閘道器的過濾器開發7.1 過濾器的執行次序
7.1 過濾器的執行次序
Spring-Cloud-Gateway 基於過濾器實現,同 zuul 類似,有pre和post兩種方式的 filter,分別處理前置邏輯和後置邏輯。客戶端的請求先經過pre型別的 filter,然後將請求轉發到具體的業務服務,收到業務服務的響應之後,再經過post型別的 filter 處理,最後返回響應到客戶端。
過濾器執行流程如下,order 越大,優先順序越低
分為全域性過濾器和區域性過濾器
全域性過濾器:對所有路由生效
2、介面用時統計
區域性過濾器:對指定路由生效
7.2定義全域性過濾器7.3定義區域性過濾器8 整合Sentinel完成流控和降級
8 整合Sentinel完成流控和降級
原文地址
[SpringCloud gateway (史上最全)](https://www.cnblogs.com/crazymakercircle/p/11704077.html)