Spring Cloud Gateway是Spring Cloud官方推出的第二代網關框架,從官網給出的對比分析結果來看,Gateway比Zuul的性能要好不少,並且功能也更加豐富。 如下是官方對比Gateway、Zuul、Linkered的分析結果,能夠看到Gateway是三個鍾效果性能最好的。 html
從官網給出的圖中能夠看到客戶端向Spring Cloud Gateway發出請求,而後網關轉發給代理的服務,而後在將服務響應的結果返回給客戶端。並且Gateway內部還有一系列的處理。java
請求進來後,會首先由Gateway Handler Mapping進行處理,這裏處理的過程當中用到 predicate,經過的請求才發送到Gateway web handler作進一步處理。而後又會通過一系列的過濾器。過濾器和Zuul的相似,也有"pre"、"post"分類。web
predicate在JDK8中的定義以下:正則表達式
Predicate 接受一個輸入參數,返回一個布爾值結果。該接口包含多種默認方法來將Predicate組合成其餘複雜的邏輯(好比:與,或,非)。能夠用於接口請求參數校驗、判斷新老數據是否有變化須要進行更新操做。add--與、or--或、negate--非spring
predicate這種輸入類型屬於Spring體系中的ServerWebExchange,它容許咱們匹配HTTP請求中的任何內容,好比請求頭或參數。並且Spring Cloud Gateway已經內置了不少Predict,這些Predict的源碼在org.springframework.cloud.gateway.handler.predicate包中。具體可參考以下:cookie
準備三個工程網絡
由於spring cloud gateway項目只支持spring boot 2.0以上版本,因此對spring cloud的版本也有要求。本文中使用的總體版本環境爲:app
(以上項目建立不在贅述,可參考案例工程),運行效果後,保證leon-gateway、leon-consumer都已經註冊在leon-eureka服務上。 框架
在leon-consumer中提供login接口,進行簡單的登陸驗證:ide
@GetMapping("/login")
public String login(@RequestParam String username, @RequestParam String password) {
if ("leon".equals(username) && "888".equals(password)) {
return "登陸成功";
}
return "登陸失敗";
}
複製代碼
在此版本上,添加註冊中心客戶端以來的包和版本
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
複製代碼
另外在leon-gateway中不能添加 web 依賴包,以下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
複製代碼
添加後啓動會報錯:
Description:
Parameter 0 of method modifyRequestBodyGatewayFilterFactory in org.springframework.cloud.gateway.config.GatewayAutoConfiguration required a bean of type 'org.springframework.http.codec.ServerCodecConfigurer' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.http.codec.ServerCodecConfigurer' in your configuration.
複製代碼
由於Spring Cloud Gateway 是使用 netty+webflux實現,webflux與web是衝突的。
接下來咱們在項目中實際使用gateway,經過gateway轉發路由,將請求轉發到leon-consumer中。
Predicate的分類比較多,接下來在項目總一一使用。
接收一個日期類型參數,在這個日期以後能夠經過。 在配置文件(application.yml)中添加
spring:
application:
name: leon-gateway
cloud:
gateway:
routes:
- id: dev
uri: http://localhost:8081/login?username=leon&password=888
predicates:
- After=2020-05-20T17:42:47.789-07:00[America/Denver]
複製代碼
如今時間爲2019-05,因此是在指定時間以前,此時啓動服務,咱們訪問http://localhost:8085/ 能夠看到轉發請求後出現404錯誤,
而後咱們修改時間爲2018
spring:
application:
name: leon-gateway
cloud:
gateway:
routes:
- id: dev
uri: http://localhost:8081/login?username=leon&password=888
predicates:
- After=2018-05-20T17:42:47.789-07:00[America/Denver]
複製代碼
重啓服務後再次訪問:http://localhost:8085/ 能夠看到已經轉發到leon-consumer的login接口,並可以收到正確返回信息
在指定時間以前才能經過轉發,具體效果相似,不在贅述
spring:
cloud:
gateway:
routes:
- id: before_route
uri: http://localhost:8081/login?username=leon&password=888
predicates:
- Before=2019-01-20T17:42:47.789-07:00[America/Denver]
複製代碼
在指定範圍時間以內的請求才能經過轉發。接收兩個日期參數,而且參數的形式,必須是較早的日期在前,較晚的日期在後,具體以下
spring:
cloud:
gateway:
routes:
- id: between_route
uri: http://localhost:8081/login?username=leon&password=888
predicates:
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
複製代碼
cookiel謂詞工廠接收兩個參數,Cookie名稱和值(也能夠是正則表達式)。請求中必須包含給定名稱的cookie,而且cookie值要符合給定的值(正則規則)才能經過轉發。 經過postman添加cookie進行測試以下。
一樣須要2個參數,一個是header名稱,另一個header值(能夠爲正則表達式),匹配經過後才轉發
- id: header_route
uri: http://localhost:8081/login?username=leon&password=888
predicates:
- Header=X-Request-Id, \d+
複製代碼
在Header中必需要有X-Request-Id名稱的參數,而且值要知足正則規則,必須爲數字
只接收一個參數,就是host name,可使用"."來進行匹配,此參數在head中添加
- id: host_route
uri: http://localhost:8081/login?username=leon&password=888
predicates:
- Host=**.leon.cn
複製代碼
接收一個參數,表明請求的類型。
- id: method_route
uri: http://localhost:8081/login?username=leon&password=888
predicates:
- Method=GET
複製代碼
此時全部的GET請求都會轉發,若是是POST請求就會404
接收一個參數,就是路徑地址(能夠爲正則表達式)
- id: path_route
uri: http://localhost:8081/login?username=leon&password=888
predicates:
- Path=/leon/{segment}
複製代碼
全部的請求路徑知足/leon/{segment}的請求將會匹配並被路由,好比/leon/1 、/leon/bar的請求,將會命中匹配,併成功轉發。
須要兩個參數,一個是參數名,一個是參數值(正則表達式)。
- id: query_route
uri: http://localhost:8081/login?username=leon&password=888
predicates:
- Query=name, leon.
複製代碼
上面配置了請求中含有參數name,而且name的值匹配leon.,則進行轉發 好比請求參數爲name=leon八、name=leono、name=leon靜均可以匹配
也能夠只加一個參數,表明只驗證參數名稱,不驗證參數值。只要包含指定名稱的參數便可經過轉發。
接收一個字符串參數,此字符串表明地址列表(最少1個),只有是以上要求的IP地址發來的請求才經過轉發
- id: remoteaddr_route
uri: http://localhost:8081/login?username=leon&password=888
predicates:
- RemoteAddr=172.17.153.1/24
複製代碼
以上配置代表只有IP地址爲172.17.153.一、172.17.153.二、172.17.153.三、。。。172.17.153.24能夠經過轉發。
能夠同時配置多個predicates,若是一個請求知足多個路由的謂詞條件時,請求只會被首個成功匹配的路由轉發。 咱們在leon-consumer項目中添加接口方法
@GetMapping("/info")
public String info() {
return "獲取信息成功";
}
複製代碼
而後在leon-gatewa重視添加兩個
spring:
application:
name: leon-gateway
cloud:
gateway:
routes:
- id: header_route
uri: http://localhost:8081/login?username=leon&password=888
predicates:
- Header=X-Request-Id, \d+
- id: query_route
uri: http://localhost:8081/info
predicates:
- Query=name, leon.
複製代碼
這裏咱們同時添加了header_route、query_route,而後咱們在postman中發送請求,同時知足兩個條件:
咱們把兩個條件順序互換:
cloud:
gateway:
routes:
- id: query_route
uri: http://localhost:8081/info
predicates:
- Query=name, leon.
- id: header_route
uri: http://localhost:8081/login?username=leon&password=888
predicates:
- Header=X-Request-Id, \d+
複製代碼
重啓服務,而後再次訪問,相同的請求此次被轉發到info接口。
固然若是設置多個路由謂詞,第一個知足優先轉發,若是第一個不知足會繼續往下判斷,遇到知足的進行轉發,咱們把請求條件改爲不合適,則準發第二個接口。
以上的配置相似"or"的條件,咱們還能夠配置組合使用,達到"and"的效果,要同時知足才能進行轉發。
cloud:
gateway:
routes:
- id: zuhe_route
uri: http://localhost:8081/info
predicates:
- Query=name, leon.
- Header=X-Request-Id, \d+
複製代碼
此時若是有一個參數設置不對,那麼就不會進行轉發
Predict決定了請求由哪個路由處理,在路由處理以前,須要通過「pre」類型的過濾器處理,處理返回響應以後,能夠由「post」類型的過濾器處理。
在Spring Cloud Gateway中,filter從做用範圍可分爲另外兩種,一種是針對於單個路由的gateway filter,它在配置文件中的寫法同predict相似;另一種是針對於全部路由的global gateway filer,全局的filter。如今來分別介紹。
GatewayFilter的使用同Predicate相似,都是在配置文件application.yml中配置便可。這裏選擇幾個經常使用介紹,更多的配置可參考官方文檔: cloud.spring.io/spring-clou…
在網關中添加Filter
cloud:
gateway:
routes:
- id: zuhe_route
uri: http://localhost:8081/info
predicates:
- Query=name, leon.
- Header=X-Request-Id, \d+
filters:
- AddRequestHeader=X-Request-Foo, Bar
複製代碼
這裏咱們添加了AddRequestHeader過濾器,在請求轉發後給HEADER中添加參數X-Request-Foo,值爲:Bar。
改造leon-consuemr工程
@GetMapping("/info")
public String info(HttpServletRequest request) {
String header = request.getHeader("X-Request-Foo");
return "獲取信息成功:" + header;
}
複製代碼
能夠看到,咱們在路由轉發後的處理方法中獲取相關參數。而後發送請求,這個請求咱們只知足路由轉發條件,並無添加X-Request-Foo的HEADER參數,可是咱們在轉發後服務處理中是能夠獲取到的。
其餘更多的設置暫不涉及,請參考官方文檔。
全局過濾器,不須要在配置文件中配置,做用在全部的路由上。Gatewqy內置的GlobalFilter以下:
若是想要本身實現GlobalFilter也能夠,實現GlobalFilter和Ordered接口便可。
public class GlobalFilter implements org.springframework.cloud.gateway.filter.GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getHeaders().getFirst("token");
if (token == null || token.isEmpty()) {
System.out.println("token爲空");
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
複製代碼
在啓動類中添加配置,將配置類加入容器管理。
@Bean
public GlobalFilter globalFilter() {
return new GlobalFilter();
}
複製代碼
此時發情請求,能夠發現若是HEADER中沒有token參數,則沒法經過轉發。
使用過Zuul的同窗都瞭解,在Zuul中能夠配置統一前置路由,好比如今咱們想把全部路徑中包含/user的都轉發到leon-consumer工程去處理。 在以上的項目中繼續改造,在leon-consumer項目中添加前綴映射:
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping("/login")
public String login(@RequestParam String username, @RequestParam String password) {
if ("leon".equals(username) && "888".equals(password)) {
return "登陸成功";
}
return "登陸失敗";
}
@GetMapping("/info")
public String info(HttpServletRequest request) {
String header = request.getHeader("X-Request-Foo");
return "獲取信息成功:" + header;
}
}
複製代碼
在leon-gateway中添加配置:
cloud:
gateway:
routes:
- id: path_route
uri: lb://leon-consumer #服務名,注意必定要以lb://開頭
predicates:
- Path=/user/{segment}
filters:
- AddRequestHeader=X-Request-Foo, Bar
discovery:
locator:
enabled: true #設置能夠經過服務名獲取服務
lower-case-service-id: true #設置獲取服務能夠經過小寫形式
複製代碼
在前面咱們的uri都是直接寫好的具體的地址,如今的服務已經註冊到Eureka上,咱們也能夠經過服務名稱找到具體的服務。 添加配置
discovery:
locator:
enabled: true #設置能夠經過服務名獲取服務
複製代碼
經過**lb:**指定便可。默認配置的名稱必須是全大寫,想要經過小寫識別,可添加配置
discovery:
locator:
lower-case-service-id: true #設置獲取服務能夠經過小寫形式
複製代碼
配置完成後,重啓服務訪問: