Spring Cloud Gateway是在Spring生態系統之上構建的API網關服務,它旨在提供一種簡單而有效的方式來對API進行路由,以及提供一些強大的過濾器功能, 例如:熔斷、限流、重試等。html
Spring Cloud Gateway 具備以下特性:spring
Spring Cloud Gateway 做爲 Spring Cloud 生態系統中的網關,目標是替代 Zuul,在Spring Cloud 2.0以上版本中,沒有對新版本的Zuul 2.0以上最新高性能版本進行集成,仍然仍是使用的 Zuul 2.0以前的非Reactor模式的老版本。(有一個版本的說法是 Zuul2.x 的連續跳票和 Zuul1.x 的性能並非很理想,從而催生了Spring Cloud Gateway。)而爲了提高網關的性能,SpringCloud Gateway是基於WebFlux框架實現的,而WebFlux框架底層則使用了高性能的Reactor模式通訊框架Netty。json
網上不少地方都說 Zuul 是阻塞的,Spring Cloud Gateway 是非阻塞的,這麼說並不嚴謹的,準確的講 Zuul1.x 是阻塞的,而在2.x的版本中,Zuul也是基於Netty,也是非阻塞的,若是必定要說性能,其實這個真沒多大差距。Zuul的有關信息能夠參考《網關Zuul科普》。cookie
Spring Cloud Gateway有如下幾個重要的概念,後面的內容也主要圍繞這幾個概念展開。架構
定義2個服務:hello-server和user-server,他們分別都註冊到euraka服務上,示例以下:app
在未通過網關時,咱們能夠經過如下2個接口來分別訪問hello-server和user-server:框架
http://localhost:8081/hello
http://localhost:8082/user
如今咱們來定義一個Spring Cloud Gateway服務,相關的Maven依賴以下:curl
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
application.yml文件中添加以下配置:ide
server:
port: 9091
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: path_route_1
uri: http://localhost:8082/user
predicates:
- Path=/user
各字段含義以下:函數
這段配置的意思是:配置了一個id爲 path_route_1的URI代理規則,當訪問地址 http://localhost:9091/user
時,會路由到地址http://localhost:8082/user
。
Spring Cloud Gateway的路由配置和Zuul的路由配置有較大的區別,玩過Zuul的注意一下二者的區分。
啓動類以下,平平無奇:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
如今咱們啓動Spring Cloud Gateway服務,而後請求http://localhost:9091/user:
hidden:~ hidden$ curl -i localhost:9091/user
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Content-Length: 11
Date: Thu, 02 Apr 2020 09:28:58 GMT
User Info!
因爲沒有配置過hello-server的服務,因此還不能經過網關訪問它:
hidden:~ hidden$ curl -i localhost:9091/hello
HTTP/1.1 404 Not Found
Content-Type: application/json
Content-Length: 133
{"timestamp":"2020-04-02T09:30:09.840+0000","path":"/hello","status":404,"error":"Not Found","message":null,"requestId":"8b0a00e1-4"}
除了使用yml配置文件的方式,還能夠經過代碼(Java Bean)來實現路由配置。咱們在啓動類中添加方法 customRouteLocator() 來定製轉發規則,詳情以下:
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;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder){
return builder.routes()
.route("path_route_2", r -> r.path("/hello")
.uri("http://localhost:8081/hello"))
.build();
}
}
重啓服務,再次訪問hello-server的接口,能夠看到訪問成功:
hidden:~ hidden$ curl -i localhost:9091/hello
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Content-Length: 19
Date: Thu, 02 Apr 2020 09:53:25 GMT
Hello!
Spring Cloud Gateway將路由匹配做爲Spring WebFlux HandlerMapping基礎架構的一部分。Spring Cloud Gateway 包括許多內置的 Predicate 工廠,這些 Predicate 工廠經過不一樣的 HTTP 請求參數來匹配,多個 Predicate 工廠能夠組合使用。
Predicate 來源於 Java 8,是 Java 8 中引入的一個函數,Predicate 接受一個輸入參數,返回一個布爾值結果。該接口包含多種默認方法來將 Predicate 組合成其餘複雜的邏輯(好比:與,或,非)。能夠用於接口請求參數校驗、判斷新老數據是否有變化須要進行更新操做。
在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性實現了各類路由匹配規則,有經過 Header、請求參數等不一樣的條件來進行做爲條件匹配到對應的路由。網上有一張圖總結了 Spring Cloud 內置的幾種 Predicate 的實現。
說白了 Predicate 就是爲了實現一組匹配規則,方便讓請求過來找到對應的 Route 進行處理,接下來咱們接下 Spring Cloud GateWay 內置幾種 Predicate 的使用。
帶有指定Cookie的請求會匹配改路由。將application.yml中的spring.cloud.gateway.routes配置修改成以下內容:
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: cookie_route
uri: http://localhost:8082/user
predicates:
- Cookie=username,pipisi
此時使用curl工具發送帶有cookie爲 username=pipisi 的請求就能夠匹配改路由:
curl -i localhost:9091/user --cookie "username=pipisi"
帶指定查詢參數的請求能夠匹配該路由。將application.yml中的spring.cloud.gateway.routes配置修改成以下內容:
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: cookie_route
uri: http://localhost:8082/user
predicates:
- Query=username
此時使用curl工具發送帶有username=pipisi 查詢參數的請求就能夠匹配改路由:
curl -i localhost:9091/user?username=pipisi
其他的還有(參考上圖):經過Header匹配(將predicates中的 -Query 換成 -Header,如下類同)、經過Host匹配(-Host)、經過請求方法匹配(-Method)、經過IP地址匹配(-RemoteAddr)、經過請求時間(-After、-Before、-Between)、經過權重(-Weight)以及最開始在入門示例中就說起的經過請求路徑匹配(-Path)。還能夠進行組合使用,好比:
predicates:
- Query=username
- Cookie=username,pipisi
- Path=/user
各類 Predicates 同時存在於同一個路由時,請求必須同時知足全部的條件才被這個路由匹配。
網關常常須要對路由請求進行過濾,執行一些操做,如鑑權以後構造頭部之類的。過濾的種類不少,如增長請求頭、增長請求參數 、增長響應頭以及斷路器等等,這就用到了Spring Cloud Gateway 的 過濾器(Filter)。
當咱們有不少服務時,好比前面所說起的user-server和hello-server,客戶端請求各個服務的API接口時,每一個服務都要作相同的事情,好比鑑權、限流、日誌等,以下圖(上半部)。
對於這樣重複的工做,能夠在微服務的上一層加一個全局的權限控制、限流、日誌的API網關服務,而後將請求轉發到具體的業務服務層。這個API網關服務就是起到一個服務邊界的做用,外界的請求訪問系統,必須先經過網關層,如上圖(下半部)。
Spring Cloud Gateway同 Zuul 相似,也有 pre
和 post
兩種方式的過濾器:pre 過濾器在請求被路由以前調用,咱們可利用這種過濾器實現身份驗證、在集羣中選擇請求的微服務、記錄調試信息等;post 過濾器在路由到微服務之後執行,這種過濾器可用來爲響應添加標準的 HTTP Header、收集統計信息和指標、將響應從微服務發送給客戶端等。
Spring Cloud Gateway的 Filter生命週期以下圖所示。
與 Zuul 不一樣的是,Filter除了分爲pre
和 post
兩種方式的 Filter 外,在Spring Cloud Gateway中,Filter 根據做用範圍可分爲另外兩種,一種是針對於單個路由的 GatewayFilter,它在配置文件中的寫法同 Predicate相似;另一種是針對於全部路由的 GlobalFilter。
過濾器容許以某種方式修改傳入的HTTP Request 或傳出的HTTP Response,它能夠限定做用在某些特定請求路徑上。Spring Cloud Gateway包含了30多個內置的GatewayFilter工廠,好比AddRequestHeaderGatewayFilterFactory(添加請求頭的過濾器工廠)、AddRequestParameterGatewayFilterFactory(添加請求參數的過濾器工廠)、AddResponseHeaderGatewayFilterFactory(添加響應頭的過濾器工廠)、HystrixGatewayFilterFactory(Hystrix熔斷過濾器工廠)、RequestRateLimiterGatewayFilterFactory(請求限流的過濾器工廠)等(所有的GatewayFilter工廠能夠查看jar包中org.springframework.cloud.gateway.filter.factory目錄,除了這些,你也能夠自定義本身的過濾器)。
GatewayFilter工廠與前面介紹的Predicate工廠相似,都是在配置文件 application.yml 中配置,遵循了約定大於配置的思想,只須要在配置文件配置GatewayFilter Factory的名稱,而不須要寫所有的類名,好比 AddRequestHeaderGatewayFilterFactory 只須要在配置文件中寫AddRequestHeader,而不是所有類名。官方文檔都給出了這些過濾器工廠詳細的使用案例,下面挑選1個案例以做演示。
application.yml配置以下:
server:
port: 9091
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
routes:
- id: cookie_route
uri: http://localhost:8082/user
filters:
- AddRequestHeader=X-Request-Foo, Bar
predicates:
- Path=/user
過濾器工廠會在匹配的請求頭加上一對請求頭「x-request-foo=Bar」。能夠在下游的user-server服務中使用@RequestHeader來查看所接收請求的請求頭信息。
Zuul 做爲網關結合註冊中心進行使用時,默認狀況下 Zuul 會根據註冊中心註冊的服務列表,以服務名爲路徑建立動態路由,Spring Cloud Gateway一樣也實現了該功能。下面咱們演示下 Spring Cloud Gateway結合註冊中心如何使用默認的動態路由。
首先添加Maven依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
其次修改application.yml配置文件:
server:
port: 9091
spring:
application:
name: spring-cloud-gateway
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
重啓服務以後,咱們就能夠像 Zuul 那樣採用下面的方式訪問接口了:
http://localhost:9091/hello-server/hello
http://localhost:9091/user-server/user
想知道更多?掃描下面的二維碼關注我