Spring Cloud Gateway:新一代API網(wǎng)關(guān)服務(wù)

Spring Cloud Gateway 為 SpringBoot 應(yīng)用提供了API網(wǎng)關(guān)支持,具有強(qiáng)大的智能路由與過濾器功能,本文將對其用法進(jìn)行詳細(xì)介紹。

Gateway 簡介

Gateway是在Spring生態(tài)系統(tǒng)之上構(gòu)建的API網(wǎng)關(guān)服務(wù),基于Spring 5,Spring Boot 2和 Project Reactor等技術(shù)。Gateway旨在提供一種簡單而有效的方式來對API進(jìn)行路由,以及提供一些強(qiáng)大的過濾器功能, 例如:熔斷、限流、重試等。

Spring Cloud Gateway 具有如下特性:

基于Spring Framework 5, Project Reactor 和 Spring Boot 2.0 進(jìn)行構(gòu)建;

動態(tài)路由:能夠匹配任何請求屬性;

可以對路由指定 Predicate(斷言)和 Filter(過濾器);

集成Hystrix的斷路器功能;

集成 Spring Cloud 服務(wù)發(fā)現(xiàn)功能;

易于編寫的 Predicate(斷言)和 Filter(過濾器);

請求限流功能;

支持路徑重寫。

相關(guān)概念

Route(路由):路由是構(gòu)建網(wǎng)關(guān)的基本模塊,它由ID,目標(biāo)URI,一系列的斷言和過濾器組成,如果斷言為true則匹配該路由;

Predicate(斷言):指的是Java 8 的 Function Predicate。 輸入類型是Spring框架中的ServerWebExchange。 這使開發(fā)人員可以匹配HTTP請求中的所有內(nèi)容,例如請求頭或請求參數(shù)。如果請求與斷言相匹配,則進(jìn)行路由;

Filter(過濾器):指的是Spring框架中GatewayFilter的實(shí)例,使用過濾器,可以在請求被路由前后對請求進(jìn)行修改。

創(chuàng)建 api-gateway模塊

這里我們創(chuàng)建一個api-gateway模塊來演示Gateway的常用功能。

在pom.xml中添加相關(guān)依賴

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-gateway</artifactId></dependency>Copy to clipboardErrorCopied

兩種不同的配置路由方式

Gateway 提供了兩種不同的方式用于配置路由,一種是通過yml文件來配置,另一種是通過Java Bean來配置,下面我們分別介紹下。

使用yml配置

在application.yml中進(jìn)行配置:

server:

? port: 9201

service-url:

? user-service: http://localhost:8201

spring:

? cloud:

? ? gateway:

? ? ? routes:

? ? ? ? - id: path_route #路由的ID

? ? ? ? ? uri: ${service-url.user-service}/user/{id} #匹配后路由地址

? ? ? ? ? predicates: # 斷言,路徑相匹配的進(jìn)行路由

? ? ? ? ? ? - Path=/user/{id}Copy to clipboardErrorCopied

啟動eureka-server,user-service和api-gateway服務(wù),并調(diào)用該地址測試:http://localhost:9201/user/1

我們發(fā)現(xiàn)該請求被路由到了user-service的該路徑上:http://localhost:8201/user/1

使用Java Bean配置

添加相關(guān)配置類,并配置一個RouteLocator對象:

/**

* Created by macro on 2019/9/24.

*/@ConfigurationpublicclassGatewayConfig{@BeanpublicRouteLocatorcustomRouteLocator(RouteLocatorBuilderbuilder){returnbuilder.routes().route("path_route2",r->r.path("/user/getByUsername").uri("http://localhost:8201/user/getByUsername")).build();}}Copy to clipboardErrorCopied

重新啟動api-gateway服務(wù),并調(diào)用該地址測試:http://localhost:9201/user/getByUsername?username=macro

我們發(fā)現(xiàn)該請求被路由到了user-service的該路徑上:http://localhost:8201/user/getByUsername?username=macro

Route Predicate 的使用

Spring Cloud Gateway將路由匹配作為Spring WebFlux HandlerMapping基礎(chǔ)架構(gòu)的一部分。 Spring Cloud Gateway包括許多內(nèi)置的Route Predicate工廠。 所有這些Predicate都與HTTP請求的不同屬性匹配。 多個Route Predicate工廠可以進(jìn)行組合,下面我們來介紹下一些常用的Route Predicate。

注意:Predicate中提到的配置都在application-predicate.yml文件中進(jìn)行修改,并用該配置啟動api-gateway服務(wù)。

After Route Predicate

在指定時間之后的請求會匹配該路由。

spring:

? cloud:

? ? gateway:

? ? ? routes:

? ? ? ? - id: after_route

? ? ? ? ? uri: ${service-url.user-service}

? ? ? ? ? predicates:

? ? ? ? ? ? - After=2019-09-24T16:30:00+08:00[Asia/Shanghai]Copy to clipboardErrorCopied

Before Route Predicate

在指定時間之前的請求會匹配該路由。

spring:

? cloud:

? ? gateway:

? ? ? routes:

? ? ? ? - id: before_route

? ? ? ? ? uri: ${service-url.user-service}

? ? ? ? ? predicates:

? ? ? ? ? ? - Before=2019-09-24T16:30:00+08:00[Asia/Shanghai]Copy to clipboardErrorCopied

Between Route Predicate

在指定時間區(qū)間內(nèi)的請求會匹配該路由。

spring:

? cloud:

? ? gateway:

? ? ? routes:

? ? ? ? - id: before_route

? ? ? ? ? uri: ${service-url.user-service}

? ? ? ? ? predicates:

? ? ? ? ? ? - Between=2019-09-24T16:30:00+08:00[Asia/Shanghai], 2019-09-25T16:30:00+08:00[Asia/Shanghai]Copy to clipboardErrorCopied

Cookie Route Predicate

帶有指定Cookie的請求會匹配該路由。

spring:

? cloud:

? ? gateway:

? ? ? routes:

? ? ? ? - id: cookie_route

? ? ? ? ? uri: ${service-url.user-service}

? ? ? ? ? predicates:

? ? ? ? ? ? - Cookie=username,macroCopy to clipboardErrorCopied

使用curl工具發(fā)送帶有cookie為username=macro的請求可以匹配該路由。

curlhttp://localhost:9201/user/1 --cookie"username=macro"Copy to clipboardErrorCopied

Header Route Predicate

帶有指定請求頭的請求會匹配該路由。

spring:

? cloud:

? ? gateway:

? ? ? routes:

? ? ? - id: header_route

? ? ? ? uri: ${service-url.user-service}

? ? ? ? predicates:

? ? ? ? - Header=X-Request-Id, \d+Copy to clipboardErrorCopied

使用curl工具發(fā)送帶有請求頭為X-Request-Id:123的請求可以匹配該路由。

curlhttp://localhost:9201/user/1 -H"X-Request-Id:123"Copy to clipboardErrorCopied

Host Route Predicate

帶有指定Host的請求會匹配該路由。

spring:

? cloud:

? ? gateway:

? ? ? routes:

? ? ? ? - id: host_route

? ? ? ? ? uri: ${service-url.user-service}

? ? ? ? ? predicates:

? ? ? ? ? ? - Host=**.macrozheng.comCopy to clipboardErrorCopied

使用curl工具發(fā)送帶有請求頭為Host:www.macrozheng.com的請求可以匹配該路由。

curlhttp://localhost:9201/user/1 -H"Host:www.macrozheng.com"Copy to clipboardErrorCopied

Method Route Predicate

發(fā)送指定方法的請求會匹配該路由。

spring:

? cloud:

? ? gateway:

? ? ? routes:

? ? ? - id: method_route

? ? ? ? uri: ${service-url.user-service}

? ? ? ? predicates:

? ? ? ? - Method=GETCopy to clipboardErrorCopied

使用curl工具發(fā)送GET請求可以匹配該路由。

curlhttp://localhost:9201/user/1Copy to clipboardErrorCopied

使用curl工具發(fā)送POST請求無法匹配該路由。

curl-X POST http://localhost:9201/user/1Copy to clipboardErrorCopied

Path Route Predicate

發(fā)送指定路徑的請求會匹配該路由。

spring:

? cloud:

? ? gateway:

? ? ? routes:

? ? ? ? - id: path_route

? ? ? ? ? uri: ${service-url.user-service}/user/{id}

? ? ? ? ? predicates:

? ? ? ? ? ? - Path=/user/{id}Copy to clipboardErrorCopied

使用curl工具發(fā)送/user/1路徑請求可以匹配該路由。

curlhttp://localhost:9201/user/1Copy to clipboardErrorCopied

使用curl工具發(fā)送/abc/1路徑請求無法匹配該路由。

curlhttp://localhost:9201/abc/1Copy to clipboardErrorCopied

Query Route Predicate

帶指定查詢參數(shù)的請求可以匹配該路由。

spring:

? cloud:

? ? gateway:

? ? ? routes:

? ? ? - id: query_route

? ? ? ? uri: ${service-url.user-service}/user/getByUsername

? ? ? ? predicates:

? ? ? ? - Query=usernameCopy to clipboardErrorCopied

使用curl工具發(fā)送帶username=macro查詢參數(shù)的請求可以匹配該路由。

curlhttp://localhost:9201/user/getByUsername?username=macroCopy to clipboardErrorCopied

使用curl工具發(fā)送帶不帶查詢參數(shù)的請求無法匹配該路由。

curlhttp://localhost:9201/user/getByUsernameCopy to clipboardErrorCopied

RemoteAddr Route Predicate

從指定遠(yuǎn)程地址發(fā)起的請求可以匹配該路由。

spring:

? cloud:

? ? gateway:

? ? ? routes:

? ? ? - id: remoteaddr_route

? ? ? ? uri: ${service-url.user-service}

? ? ? ? predicates:

? ? ? ? - RemoteAddr=192.168.1.1/24Copy to clipboardErrorCopied

使用curl工具從192.168.1.1發(fā)起請求可以匹配該路由。

curlhttp://localhost:9201/user/1Copy to clipboardErrorCopied

Weight Route Predicate

使用權(quán)重來路由相應(yīng)請求,以下表示有80%的請求會被路由到localhost:8201,20%會被路由到localhost:8202。

spring:

? cloud:

? ? gateway:

? ? ? routes:

? ? ? - id: weight_high

? ? ? ? uri: http://localhost:8201

? ? ? ? predicates:

? ? ? ? - Weight=group1, 8

? ? ? - id: weight_low

? ? ? ? uri: http://localhost:8202

? ? ? ? predicates:

? ? ? ? - Weight=group1, 2Copy to clipboardErrorCopied

Route Filter 的使用

路由過濾器可用于修改進(jìn)入的HTTP請求和返回的HTTP響應(yīng),路由過濾器只能指定路由進(jìn)行使用。Spring Cloud Gateway 內(nèi)置了多種路由過濾器,他們都由GatewayFilter的工廠類來產(chǎn)生,下面我們介紹下常用路由過濾器的用法。

AddRequestParameter GatewayFilter

給請求添加參數(shù)的過濾器。

spring:

? cloud:

? ? gateway:

? ? ? routes:

? ? ? ? - id: add_request_parameter_route

? ? ? ? ? uri: http://localhost:8201

? ? ? ? ? filters:

? ? ? ? ? ? - AddRequestParameter=username, macro

? ? ? ? ? predicates:

? ? ? ? ? ? - Method=GETCopy to clipboardErrorCopied

以上配置會對GET請求添加username=macro的請求參數(shù),通過curl工具使用以下命令進(jìn)行測試。

curlhttp://localhost:9201/user/getByUsernameCopy to clipboardErrorCopied

相當(dāng)于發(fā)起該請求:

curlhttp://localhost:8201/user/getByUsername?username=macroCopy to clipboardErrorCopied

StripPrefix GatewayFilter

對指定數(shù)量的路徑前綴進(jìn)行去除的過濾器。

spring:

? cloud:

? ? gateway:

? ? ? routes:

? ? ? - id: strip_prefix_route

? ? ? ? uri: http://localhost:8201

? ? ? ? predicates:

? ? ? ? - Path=/user-service/**

? ? ? ? filters:

? ? ? ? - StripPrefix=2Copy to clipboardErrorCopied

以上配置會把以/user-service/開頭的請求的路徑去除兩位,通過curl工具使用以下命令進(jìn)行測試。

curlhttp://localhost:9201/user-service/a/user/1Copy to clipboardErrorCopied

相當(dāng)于發(fā)起該請求:

curlhttp://localhost:8201/user/1Copy to clipboardErrorCopied

PrefixPath GatewayFilter

與StripPrefix過濾器恰好相反,會對原有路徑進(jìn)行增加操作的過濾器。

spring:

? cloud:

? ? gateway:

? ? ? routes:

? ? ? - id: prefix_path_route

? ? ? ? uri: http://localhost:8201

? ? ? ? predicates:

? ? ? ? - Method=GET

? ? ? ? filters:

? ? ? ? - PrefixPath=/userCopy to clipboardErrorCopied

以上配置會對所有GET請求添加/user路徑前綴,通過curl工具使用以下命令進(jìn)行測試。

curlhttp://localhost:9201/1Copy to clipboardErrorCopied

相當(dāng)于發(fā)起該請求:

curlhttp://localhost:8201/user/1Copy to clipboardErrorCopied

Hystrix GatewayFilter

Hystrix 過濾器允許你將斷路器功能添加到網(wǎng)關(guān)路由中,使你的服務(wù)免受級聯(lián)故障的影響,并提供服務(wù)降級處理。

要開啟斷路器功能,我們需要在pom.xml中添加Hystrix的相關(guān)依賴:

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix</artifactId></dependency>Copy to clipboardErrorCopied

然后添加相關(guān)服務(wù)降級的處理類:

/**

* Created by macro on 2019/9/25.

*/@RestControllerpublicclassFallbackController{@GetMapping("/fallback")publicObjectfallback(){Map<String,Object>result=newHashMap<>();result.put("data",null);result.put("message","Get request fallback!");result.put("code",500);returnresult;}}Copy to clipboardErrorCopied

在application-filter.yml中添加相關(guān)配置,當(dāng)路由出錯時會轉(zhuǎn)發(fā)到服務(wù)降級處理的控制器上:

spring:

? cloud:

? ? gateway:

? ? ? routes:

? ? ? ? - id: hystrix_route

? ? ? ? ? uri: http://localhost:8201

? ? ? ? ? predicates:

? ? ? ? ? ? - Method=GET

? ? ? ? ? filters:

? ? ? ? ? ? - name: Hystrix

? ? ? ? ? ? ? args:

? ? ? ? ? ? ? ? name: fallbackcmd

? ? ? ? ? ? ? ? fallbackUri: forward:/fallbackCopy to clipboardErrorCopied

關(guān)閉user-service,調(diào)用該地址進(jìn)行測試:http://localhost:9201/user/1?,發(fā)現(xiàn)已經(jīng)返回了服務(wù)降級的處理信息。

RequestRateLimiter GatewayFilter

RequestRateLimiter 過濾器可以用于限流,使用RateLimiter實(shí)現(xiàn)來確定是否允許當(dāng)前請求繼續(xù)進(jìn)行,如果請求太大默認(rèn)會返回HTTP 429-太多請求狀態(tài)。

在pom.xml中添加相關(guān)依賴:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis-reactive</artifactId></dependency>Copy to clipboardErrorCopied

添加限流策略的配置類,這里有兩種策略一種是根據(jù)請求參數(shù)中的username進(jìn)行限流,另一種是根據(jù)訪問IP進(jìn)行限流;

/**

* Created by macro on 2019/9/25.

*/@ConfigurationpublicclassRedisRateLimiterConfig{@BeanKeyResolveruserKeyResolver(){returnexchange->Mono.just(exchange.getRequest().getQueryParams().getFirst("username"));}@BeanpublicKeyResolveripKeyResolver(){returnexchange->Mono.just(exchange.getRequest().getRemoteAddress().getHostName());}}Copy to clipboardErrorCopied

我們使用Redis來進(jìn)行限流,所以需要添加Redis和RequestRateLimiter的配置,這里對所有的GET請求都進(jìn)行了按IP來限流的操作;

server:

? port: 9201

spring:

? redis:

? ? host: localhost

? ? password: 123456

? ? port: 6379

? cloud:

? ? gateway:

? ? ? routes:

? ? ? ? - id: requestratelimiter_route

? ? ? ? ? uri: http://localhost:8201

? ? ? ? ? filters:

? ? ? ? ? ? - name: RequestRateLimiter

? ? ? ? ? ? ? args:

? ? ? ? ? ? ? ? redis-rate-limiter.replenishRate: 1 #每秒允許處理的請求數(shù)量

? ? ? ? ? ? ? ? redis-rate-limiter.burstCapacity: 2 #每秒最大處理的請求數(shù)量

? ? ? ? ? ? ? ? key-resolver: "#{@ipKeyResolver}" #限流策略,對應(yīng)策略的Bean

? ? ? ? ? predicates:

? ? ? ? ? ? - Method=GET

logging:

? level:

? ? org.springframework.cloud.gateway: debugCopy to clipboardErrorCopied

多次請求該地址:http://localhost:9201/user/1?,會返回狀態(tài)碼為429的錯誤;

Retry GatewayFilter

對路由請求進(jìn)行重試的過濾器,可以根據(jù)路由請求返回的HTTP狀態(tài)碼來確定是否進(jìn)行重試。

修改配置文件:

spring:

? cloud:

? ? gateway:

? ? ? routes:

? ? ? - id: retry_route

? ? ? ? uri: http://localhost:8201

? ? ? ? predicates:

? ? ? ? - Method=GET

? ? ? ? filters:

? ? ? ? - name: Retry

? ? ? ? ? args:

? ? ? ? ? ? retries: 1 #需要進(jìn)行重試的次數(shù)

? ? ? ? ? ? statuses: BAD_GATEWAY #返回哪個狀態(tài)碼需要進(jìn)行重試,返回狀態(tài)碼為5XX進(jìn)行重試

? ? ? ? ? ? backoff:

? ? ? ? ? ? ? firstBackoff: 10ms

? ? ? ? ? ? ? maxBackoff: 50ms

? ? ? ? ? ? ? factor: 2

? ? ? ? ? ? ? basedOnPreviousValue: falseCopy to clipboardErrorCopied

當(dāng)調(diào)用返回500時會進(jìn)行重試,訪問測試地址:http://localhost:9201/user/111

可以發(fā)現(xiàn)user-service控制臺報錯2次,說明進(jìn)行了一次重試。

2019-10-2714:08:53.435 ERROR2280---[nio-8201-exec-2]o.a.c.c.C.[.[.[/].[dispatcherServlet]:Servlet.service()forservlet[dispatcherServlet]incontext with path[]threw exception[Request processing failed;nested exception is java.lang.NullPointerException]with root causejava.lang.NullPointerException: null? ? at com.macro.cloud.controller.UserController.getUser(UserController.java:34)~[classes/:na]Copy to clipboardErrorCopied

結(jié)合注冊中心使用

我們上次講到使用Zuul作為網(wǎng)關(guān)結(jié)合注冊中心進(jìn)行使用時,默認(rèn)情況下Zuul會根據(jù)注冊中心注冊的服務(wù)列表,以服務(wù)名為路徑創(chuàng)建動態(tài)路由,Gateway同樣也實(shí)現(xiàn)了該功能。下面我們演示下Gateway結(jié)合注冊中心如何使用默認(rèn)的動態(tài)路由和過濾器。

使用動態(tài)路由

在pom.xml中添加相關(guān)依賴:

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-eureka-client</artifactId></dependency>Copy to clipboardErrorCopied

添加application-eureka.yml配置文件:

server:

? port: 9201

spring:

? application:

? ? name: api-gateway

? cloud:

? ? gateway:

? ? ? discovery:

? ? ? ? locator:

? ? ? ? ? enabled: true #開啟從注冊中心動態(tài)創(chuàng)建路由的功能

? ? ? ? ? lower-case-service-id: true #使用小寫服務(wù)名,默認(rèn)是大寫

eureka:

? client:

? ? service-url:

? ? ? defaultZone: http://localhost:8001/eureka/

logging:

? level:

? ? org.springframework.cloud.gateway: debugCopy to clipboardErrorCopied

使用application-eureka.yml配置文件啟動api-gateway服務(wù),訪問http://localhost:9201/user-service/user/1?,可以路由到user-service的http://localhost:8201/user/1?處。

使用過濾器

在結(jié)合注冊中心使用過濾器的時候,我們需要注意的是uri的協(xié)議為lb,這樣才能啟用Gateway的負(fù)載均衡功能。

修改application-eureka.yml文件,使用了PrefixPath過濾器,會為所有GET請求路徑添加/user路徑并路由;

server:

? port: 9201

spring:

? application:

? ? name: api-gateway

? cloud:

? ? gateway:

? ? ? routes:

? ? ? ? - id: prefixpath_route

? ? ? ? ? uri: lb://user-service #此處需要使用lb協(xié)議

? ? ? ? ? predicates:

? ? ? ? ? ? - Method=GET

? ? ? ? ? filters:

? ? ? ? ? ? - PrefixPath=/user

? ? ? discovery:

? ? ? ? locator:

? ? ? ? ? enabled: true

eureka:

? client:

? ? service-url:

? ? ? defaultZone: http://localhost:8001/eureka/

logging:

? level:

? ? org.springframework.cloud.gateway: debugCopy to clipboardErrorCopied

使用application-eureka.yml配置文件啟動api-gateway服務(wù),訪問http://localhost:9201/1?,可以路由到user-service的http://localhost:8201/user/1?處。

使用到的模塊

springcloud-learning

├── eureka-server -- eureka注冊中心

├── user-service -- 提供User對象CRUD接口的服務(wù)

└── api-gateway -- gateway作為網(wǎng)關(guān)的測試服務(wù)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容