一、什么是springcloud gateway?
Spring Cloud Gateway 旨在為微服務架構提供一種簡單有效的、統一的 API 路由管理方式。
Spring Cloud Gateway 作為 Spring Cloud 生態系中的網關,它不僅提供統一的路由方式,並且基於 Filter 鏈的方式提供了網關基本的功能,例如:安全、監控/埋點和限流等。
Spring Cloud Gateway 依賴 Spring Boot 和 Spring WebFlux,基於 Netty 運行。它不能在傳統的 servlet 容器中工作,也不能構建成 war 包。
在 Spring Cloud Gateway 中有如下幾個核心概念需要我們了解:
1)Route
Route 是網關的基礎元素,由 ID、目標 URI、斷言、過濾器組成。當請求到達網關時,由 Gateway Handler Mapping 通過斷言進行路由匹配(Mapping),當斷言為真時,匹配到路由。
2)Predicate
Predicate 是 Java 8 中提供的一個函數。輸入類型是 Spring Framework ServerWebExchange。它允許開發人員匹配來自 HTTP 的請求,例如請求頭或者請求參數。簡單來說它就是匹配條件。
3)Filter
Filter 是 Gateway 中的過濾器,可以在請求發出前后進行一些業務上的處理。
Spring Cloud Gateway 工作原理
Gateway的客戶端回向Spring Cloud Gateway發起請求,請求首先會被HttpWebHandlerAdapter進行提取組裝成網關的上下文,然后網關的上下文會傳遞到DispatcherHandler。DispatcherHandler是所有請求的分發處理器,DispatcherHandler主要負責分發請求對應的處理器,比如將請求分發到對應RoutePredicateHandlerMapping(路由斷言處理器映射器)。路由斷言處理映射器主要用於路由的查找,以及找到路由后返回對應的FilteringWebHandler。FilteringWebHandler主要負責組裝Filter鏈表並調用Filter執行一系列Filter處理,然后把請求轉到后端對應的代理服務處理,處理完畢后,將Response返回到Gateway客戶端。
在Filter鏈中,通過虛線分割Filter的原因是,過濾器可以在轉發請求之前處理或者接收到被代理服務的返回結果之后處理。所有的Pre類型的Filter執行完畢之后,才會轉發請求到被代理的服務處理。被代理的服務把所有請求完畢之后,才會執行Post類型的過濾器。
博客摘錄自《重新定義Spring Cloud實戰》。
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。
SpringCloud Gateway和架構
Spring在2017年下半年迎來了Webflux,Webflux的出現填補了Spring在響應式編程上的空白,Webflux的響應式編程不僅僅是編程風格的改變,而且對於一系列的著名框架,都提供了響應式訪問的開發包,比如Netty、Redis等等。
SpringCloud Gateway 使用的Webflux中的reactor-netty響應式編程組件,底層使用了Netty通訊框架。
- 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.2 Webflux模型
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高並發實戰》一書,這里不做過多的贅述。
Spring Cloud Gateway的處理流程
客戶端向 Spring Cloud Gateway 發出請求。然后在 Gateway Handler Mapping 中找到與請求相匹配的路由,將其發送到 Gateway Web Handler。Handler 再通過指定的過濾器鏈來將請求發送到我們實際的服務執行業務邏輯,然后返回。過濾器之間用虛線分開是因為過濾器可能會在發送代理請求之前(“pre”)或之后(“post”)執行業務邏輯。
Spring Cloud Gateway路由配置方式
基礎URI一種路由配置方式
spring: application: name: cloud-gateway cloud: gateway: discovery: locator: enabled: true #開啟從注冊中心動態創建路由的功能,利用微服務名進行路由 routes: - id: payment_routh #payment_route #路由的ID,沒有固定規則但要求唯一,建議配合服務名 uri: http://localhost:8001 #匹配后提供服務的路由地址 predicates: - Path=/payment/get/** # 斷言,路徑相匹配的進行路由
基於代碼的路由配置方式
/** * 編程式路由 * @author L * */ @Configuration public class GateWayConfig { @Bean public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) { RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes(); routes.route("path_route_atguigu", r -> r.path("/guonei") .uri("http://news.baidu.com/guonei")).build(); return routes.build(); } }
和注冊中心相結合的路由配置方式
在uri的schema協議部分為自定義的lb:類型,表示從微服務注冊中心(如Eureka)訂閱服務,並且進行服務的路由。
一個典型的示例如下:
spring: application: name: cloud-gateway cloud: gateway: discovery: locator: enabled: true #開啟從注冊中心動態創建路由的功能,利用微服務名進行路由 routes: - id: payment_routh #payment_route #路由的ID,沒有固定規則但要求唯一,建議配合服務名 #uri: http://localhost:8001 #匹配后提供服務的路由地址 uri: lb://cloud-payment-service #匹配后提供服務的路由地址 predicates: - Path=/payment/get/** # 斷言,路徑相匹配的進行路由
注冊中心相結合的路由配置方式,與單個URI的路由配置,區別其實很小,僅僅在於URI的schema協議不同。單個URI的地址的schema協議,一般為http或者https協議。
詳解:SpringCloud Gateway 匹配規則
Spring Cloud Gateway 的功能很強大,我們僅僅通過 Predicates 的設計就可以看出來,前面我們只是使用了 predicates 進行了簡單的條件匹配,其實 Spring Cloud Gataway 幫我們內置了很多 Predicates 功能。
Spring Cloud Gateway 是通過 Spring WebFlux 的 HandlerMapping 做為底層支持來匹配到轉發路由,Spring Cloud Gateway 內置了很多 Predicates 工廠,這些 Predicates 工廠通過不同的 HTTP 請求參數來匹配,多個 Predicates 工廠可以組合使用。
Predicate 斷言條件介紹
Predicate 來源於 Java 8,是 Java 8 中引入的一個函數,Predicate 接受一個輸入參數,返回一個布爾值結果。該接口包含多種默認方法來將 Predicate 組合成其他復雜的邏輯(比如:與,或,非)。可以用於接口請求參數校驗、判斷新老數據是否有變化需要進行更新操作。
在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性實現了各種路由匹配規則,有通過 Header、請求參數等不同的條件來進行作為條件匹配到對應的路由。網上有一張圖總結了 Spring Cloud 內置的幾種 Predicate 的實現。
[
說白了 Predicate 就是為了實現一組匹配規則,方便讓請求過來找到對應的 Route 進行處理,接下來我們接下 Spring Cloud GateWay 內置幾種 Predicate 的使用。
通過請求參數匹配,請求參數在以上圖中,換個屬性即可
spring: cloud: gateway: routes: - id: method_route uri: http://www.google.com predicates: - Method=GET
GatewayFilter 工廠介紹
Route filters可以通過一些方式修改HTTP請求的輸入和輸出,針對某些特殊的場景,Spring Cloud Gateway已經內置了很多不同功能的GatewayFilter Factories。
下面就來通過例子逐一講解這些GatewayFilter Factories。
1. AddRequestHeader GatewayFilter Factory
AddRequestHeader GatewayFilter Factory通過配置name和value可以增加請求的header
spring: application: name: cloud-gateway cloud: gateway: discovery: locator: enabled: true #開啟從注冊中心動態創建路由的功能,利用微服務名進行路由 routes: - id: payment_routh #payment_route #路由的ID,沒有固定規則但要求唯一,建議配合服務名 #uri: http://localhost:8001 #匹配后提供服務的路由地址 uri: lb://cloud-payment-service #匹配后提供服務的路由地址 predicates: - Path=/payment/get/** # 斷言,路徑相匹配的進行路由 filters: - AddRequestHeader=foo, aaa - AddRequestParameter=foo, bbbccc - name: Hystrix # Hystrix Filter的名稱、設置成默認的 args: # Hystrix 配置參數 name: fallbackcmd # HystrixCommand 的名字 fallbackUri: forward:/fallbackA # fallback對用的uri
2、在多應的微服務中 HttpServletRequest.getHeader("foo");獲取對應的值
自定義過濾器:需要實現GlobalFilter,Ordered 接口
@Component @Slf4j public class MyLogGateWayFilter implements GlobalFilter,Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { log.info("***********come in MyLogGateWayFilter: "+new Date()); String uname = exchange.getRequest().getQueryParams().getFirst("uname"); if(uname == null) { log.info("*******用戶名為null"); exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } @Override public int getOrder() { return 0; } }
實現熔斷降級
為什么要實現熔斷降級?
在分布式系統中,網關作為流量的入口,因此會有大量的請求進入網關,向其他服務發起調用,其他服務不可避免的會出現調用失敗(超時、異常),失敗時不能讓請求堆積在網關上,需要快速失敗並返回給客戶端,想要實現這個要求,就必須在網關上做熔斷、降級操作。
為什么在網關上請求失敗需要快速返回給客戶端?
因為當一個客戶端請求發生故障的時候,這個請求會一直堆積在網關上,當然只有一個這種請求,網關肯定沒有問題(如果一個請求就能造成整個系統癱瘓,那這個系統可以下架了),但是網關上堆積多了就會給網關乃至整個服務都造成巨大的壓力,甚至整個服務宕掉。因此要對一些服務和頁面進行有策略的降級,以此緩解服務器資源的的壓力,以保證核心業務的正常運行,同時也保持了客戶和大部分客戶的得到正確的相應,所以需要網關上請求失敗需要快速返回給客戶端。
文件配置:
spring: application: name: cloud-gateway cloud: gateway: discovery: locator: enabled: true #開啟從注冊中心動態創建路由的功能,利用微服務名進行路由 routes: - id: payment_routh #payment_route #路由的ID,沒有固定規則但要求唯一,建議配合服務名 #uri: http://localhost:8001 #匹配后提供服務的路由地址 uri: lb://cloud-payment-service #匹配后提供服務的路由地址 predicates: - Path=/payment/get/** # 斷言,路徑相匹配的進行路由 filters: - AddRequestHeader=foo, yinjihuan - AddRequestParameter=foo, yinjihuanAddRequestParameter - name: Hystrix # Hystrix Filter的名稱、設置成默認的 args: # Hystrix 配置參數 name: fallbackcmd # HystrixCommand 的名字 fallbackUri: forward:/fallbackA # fallback對用的uri # Hystrix 配置 hystrix: fallbackcmd: execution: isolation: thread: timeoutInMilliseconds: 1000 # Hystrix 的 fallbackcmd 時間
熔斷的fallbackA
@RestController public class FallbackController { @GetMapping("/fallbackA") public CommonResult fallbackA() { return new CommonResult(100,"服務暫時不可用"); } }
過濾器Hystrix,作用是通過Hystrix進行熔斷降級
當上游的請求,進入了Hystrix熔斷降級機制時,就會調用fallbackUri配置的降級地址。需要注意的是,還需要單獨設置Hystrix的commandKey的超時時間