第二代網關GateWay搭建流程

  • 2019 年 12 月 19 日
  • 筆記

Spring Cloud第二代網關GateWay是由純Netty開發,底層為Reactor,WebFlux構建,不依賴任何Servlet容器,它不同於Zuul,使用的是非同步IO,性能較Zuul提升1.6倍。搭建過程如下(本次搭建的為子項目,主項目可以參考Nacos搭建流程 )

pom

<dependency>     <groupId>org.springframework.cloud</groupId>     <artifactId>spring-cloud-starter-gateway</artifactId>  </dependency>  <dependency>     <groupId>com.alibaba.cloud</groupId>     <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>  </dependency>

配置文件

server:    port: 8040  spring:    application:      name: gateway    cloud:      nacos:        discovery:          server-addr: 127.0.0.1:8848      gateway:        discovery:          locator:            enabled: true

以上的意思不僅是把自己給註冊到nacos,並且獲取nacos的所有註冊服務。

啟動網關項目,現在就可以進行網路路由了。訪問格式為 ip:埠/服務註冊名/restfulapi-url

比方說我們現在有兩個微服務項目,一個為user(埠8082),一個為nacos(埠8081).

三大核心概念

  • Route(路由) Spring Cloud Gateway的基礎元素,可簡單理解成一條轉發的規則。包含:ID,目標的URL,Predicate集合以及Filter集合。
  • Predicate(謂詞) 即java.util.function.Predicate,Spring Cloud Gateway使用Predicate實現路由的匹配條件。這是一個可以進行條件判斷的函數式介面,具體可以參考本人部落格Java函數式編程整理
  • Filter(過濾器) 修改請求以及響應。

由於我們使用了nacos來進行服務發現,所以我們使用了之前的配置文件,但如果不使用服務發現,只做常規的轉發如下

spring:    cloud:      gateway:        routes:          - id: some_route            uri: http://www.baidu.com            predicates:              - Path=/user/1            filtes:              - AddRequestHeader=X-Request-Foo, Bar

這段配置的意思是說,當我們請求/user/1的url的時候,會添加AddRequestHeader=X-Request-Foo, Bar過濾器做一些處理,然後路由到http://www.baidu.com。

路由謂詞配置工廠

路由謂詞配置工廠由一整套謂詞來進行配置轉發的不同情況。

謂詞工廠

備註

After

此謂詞匹配當前日期時間之後發生的請求。

Before

此謂詞匹配在當前日期時間之前發生的請求。

Between

此謂詞匹配datetime1之後和datetime2之前發生的請求。 datetime2參數必須在datetime1之後。

Cookie

Cookie Route Predicate Factory有兩個參數,cookie名稱和正則表達式。此謂詞匹配具有給定名稱且值與正則表達式匹配的cookie。

Header

Header Route Predicate Factory有兩個參數,標題名稱和正則表達式。與具有給定名稱且值與正則表達式匹配的標頭匹配。

Host

Host Route Predicate Factory採用一個參數:主機名模式。該模式是一種Ant樣式模式「.」作為分隔符。此謂詞匹配與模式匹配的Host標頭。

Method

Method Route Predicate Factory採用一個參數:要匹配的HTTP方法。

Path

匹配請求的path

Query

Query Route Predicate Factory有兩個參數:一個必需的參數和一個可選的正則表達式。

RemoteAddr

RemoteAddr Route Predicate Factory採用CIDR符號(IPv4或IPv6)字元串的列表(最小值為1),例如, 192.168.0.1/16(其中192.168.0.1是IP地址,16是子網掩碼)。

路由到指定URL

  • 通配

現在我們去掉nacos的配置,不由nacos來發現

spring:    application:      name: gateway    cloud:      gateway:        routes:        - id: gate          uri: http://127.0.0.1:8082          predicates:          #由/user來匹配跳轉          - Path=/user/**          filters:          #跳轉後省略第一個通配          - StripPrefix=1

此時訪問

將跳轉到

  • 謂詞After
spring:    application:      name: gateway    cloud:      gateway:        routes:        - id: gate          uri: http://127.0.0.1:8082          predicates:          #由/user來匹配跳轉          - Path=/user/**          #在2019-12-14日20:26後允許該轉發          - After=2019-12-14T20:26:15.667+08:00[Asia/Shanghai]          filters:          #跳轉後省略第一個通配          - StripPrefix=1

這裡表示在該時間後允許轉發,如果我們將該時間設置為

spring:    application:      name: gateway    cloud:      gateway:        routes:        - id: gate          uri: http://127.0.0.1:8082          predicates:          #由/user來匹配跳轉          - Path=/user/**          #在2019-12-15日20:26後允許該轉發          - After=2019-12-15T20:26:15.667+08:00[Asia/Shanghai]          filters:          #跳轉後省略第一個通配          - StripPrefix=1

則轉發失敗,返回404

我們可以通過以下方法來獲取這裡的時間設置

public class TimeTest {      public static void main(String[] args) {          System.out.println(ZonedDateTime.now());      }  }

運行結果

2019-12-14T20:43:34.755+08:00[Asia/Shanghai]

  • 謂詞Before

現在我們將上面的15號改為Before

spring:    application:      name: gateway    cloud:      gateway:        routes:        - id: gate          uri: http://127.0.0.1:8082          predicates:          #由/user來匹配跳轉          - Path=/user/**          #在2019-12-15日20:26前允許該轉發          - Before=2019-12-15T20:26:15.667+08:00[Asia/Shanghai]          filters:          #跳轉後省略第一個通配          - StripPrefix=1

此時就可以正常轉發,而改成14號則會失敗。

  • 謂詞Between
spring:    application:      name: gateway    cloud:      gateway:        routes:        - id: gate          uri: http://127.0.0.1:8082          predicates:          #由/user來匹配跳轉          - Path=/user/**          #在2019-12-14日20:26到2019-12-15日20:26之間允許該轉發          - Between=2019-12-14T20:26:15.667+08:00[Asia/Shanghai],2019-12-15T20:26:15.667+08:00[Asia/Shanghai]          filters:          #跳轉後省略第一個通配          - StripPrefix=1
  • 謂詞Cookie

我們在user模組增加一個帶cookie的Controller

@Slf4j  @RestController  public class CookieController {      @GetMapping("/welcome")      public Boolean handle(HttpServletRequest request,                                 HttpServletResponse response) throws Exception {          Cookie cookie = new Cookie("test","value");          cookie.setMaxAge(Integer.MAX_VALUE);          response.addCookie(cookie);          log.info("welcome");          return true;      }  }

此時我們訪問該Controller為

此時網關這邊配置為

spring:    application:      name: gateway    cloud:      gateway:        routes:        - id: gate          uri: http://127.0.0.1:8082          predicates:          #由/user來匹配跳轉          - Path=/user/**          #只有帶上Cookie名為test,並且值符合正則value的cookie時,才允許被轉發          - Cookie=test,value          filters:          #跳轉後省略第一個通配          - StripPrefix=1
  • 謂詞Header

現在我們給user模組添加一個Controller的方法

@GetMapping("/header")  public String header(@RequestHeader("item") String item) {      return item;  }

我們通過postman給該方法的訪問添加請求頭

在網關中的配置為

spring:    application:      name: gateway    cloud:      gateway:        routes:        - id: gate          uri: http://127.0.0.1:8082          predicates:          #由/user來匹配跳轉          - Path=/user/**          #只有帶上請求頭名為item,並且值符合正則123.p,才會轉發          - Header=item,123.p          filters:          #跳轉後省略第一個通配          - StripPrefix=1

這裡正則.可以匹配一個單字元

如果我們在請求頭item中設置錯誤的字元則無法轉發

  • 謂詞Host

要配置Host,我們需要給伺服器的hosts文件添加一個域名映射,當然在互聯網上需要一個域名來做DNS解析。

我這裡給自己的域名添加為local.register.com

訪問user的find方法

給網關添加配置

spring:    application:      name: gateway    cloud:      gateway:        routes:        - id: gate          uri: http://127.0.0.1:8082          predicates:          #由/user來匹配跳轉          - Path=/user/**          #只有帶上請求頭Host,且值匹配**.register.com:8040才能通過轉發          - Host=**.register.com:8040          filters:          #跳轉後省略第一個通配          - StripPrefix=1

此時我們通過網關訪問如下

  • 謂詞Method
spring:    application:      name: gateway    cloud:      gateway:        routes:        - id: gate          uri: http://127.0.0.1:8082          predicates:          #由/user來匹配跳轉          - Path=/user/**          #只有當HTTP請求方法是GET時才能轉發          - Method=GET          filters:          #跳轉後省略第一個通配          - StripPrefix=1
  • 謂詞Query

現在我們給user模組增加一個Controller方法

@GetMapping("/query")  public String query(@RequestParam("name") String name) {      return name;  }

訪問如下

網關配置如下

spring:    application:      name: gateway    cloud:      gateway:        routes:        - id: gate          uri: http://127.0.0.1:8082          predicates:          #由/user來匹配跳轉          - Path=/user/**          #只有當請求帶上參數名稱為name時才能通過轉發          - Query=name          filters:          #跳轉後省略第一個通配          - StripPrefix=1

如果不帶上該參數則無法轉發,如

  • 謂詞RemoteAddr
spring:    application:      name: gateway    cloud:      gateway:        routes:        - id: gate          uri: http://127.0.0.1:8082          predicates:          #由/user來匹配跳轉          - Path=/user/**          #只有當請求為192.168.20.1/24網段(IP地址從192.168.20.1到192.168.20.254)才會轉發          - RemoteAddr=192.168.20.1/24          filters:          #跳轉後省略第一個通配          - StripPrefix=1

例如

但是使用127.0.0.1卻無法訪問

現在我們恢復nacos的服務發現

spring:    application:      name: gateway    cloud:      nacos:        discovery:          server-addr: 127.0.0.1:8848      gateway:        routes:        - id: gate          uri: lb://user          predicates:          #由/user-center來匹配跳轉          - Path=/user-center/**          filters:          #跳轉後省略第一個通配          - StripPrefix=1

為了跟不做任何配置相區別,我們這裡謂詞Path寫了user-center

自定義路由謂詞工廠

假設現在我們的一個API只有在上午9點到下午5點允許轉發

配置的文件如下

spring:    application:      name: gateway    cloud:      nacos:        discovery:          server-addr: 127.0.0.1:8848      gateway:        routes:        - id: gate          uri: lb://user          predicates:          #由/user-center來匹配跳轉          - Path=/user-center/**          - TimeBetween=上午9:00,下午5:00          filters:          #跳轉後省略第一個通配          - StripPrefix=1

由於這個TimeBetween並不是gateway默認的謂詞工廠,所以我們需要自己來實現一個謂詞工廠,我們先定義一個時間的配置類

@Data  public class TimeBetweenConfig {      private LocalTime start;      private LocalTime end;  }

然後自定義一個謂詞工廠類,該工廠類名稱必須以自定義謂詞開頭(這裡是TimeBetween),以RoutePredicateFactory結尾,並繼承AbstractRoutePredicateFactory抽象類

@Component  public class TimeBetweenRoutePredicateFactory extends AbstractRoutePredicateFactory<TimeBetweenConfig>{      public TimeBetweenRoutePredicateFactory() {          super(TimeBetweenConfig.class);      }        @Override      public Predicate<ServerWebExchange> apply(TimeBetweenConfig config) {          LocalTime start = config.getStart();          LocalTime end = config.getEnd();          return exchange -> {              LocalTime now = LocalTime.now();              return now.isAfter(start) && now.isBefore(end);          };      }        @Override      public List<String> shortcutFieldOrder() {          return Arrays.asList("start","end");      }  }

內置過濾器工廠

1 AddRequestHeader GatewayFilter Factory 2 AddRequestParameter GatewayFilter Factory 3 AddResponseHeader GatewayFilter Factory 4 DedupeResponseHeader GatewayFilter Factory 5 Hystrix GatewayFilter Factory 6 FallbackHeaders GatewayFilter Factory

7 PrefixPath GatewayFilter Factory

8 PreserveHostHeader GatewayFilter Factory 9 RequestRateLimiter GatewayFilter Factory 10 RedirectTo GatewayFilter Factory 11 RemoveHopByHopHeadersFilter GatewayFilter Factory 12 RemoveRequestHeader GatewayFilter Factory 13 RemoveResponseHeader GatewayFilter Factory 14 RewritePath GatewayFilter Factory 15 RewriteResponseHeader GatewayFilter Factory 16 SaveSession GatewayFilter Factory 17 SecureHeaders GatewayFilter Factory 18 SetPath GatewayFilter Factory 19 SetResponseHeader GatewayFilter Factory 20 SetStatus GatewayFilter Factory 21 StripPrefix GatewayFilter Factory 22 Retry GatewayFilter Factory 23 RequestSize GatewayFilter Factory 24 Modify Request Body GatewayFilter Factory 25 Modify Response Body GatewayFilter Factory 26 Default Filters

  • AddRequestHeader
spring:    application:      name: gateway    cloud:      nacos:        discovery:          server-addr: 192.168.10.172:8848      gateway:        routes:        - id: gate          uri: lb://user          predicates:          #由/user-center來匹配跳轉          - Path=/user-center/**          filters:          #跳轉後省略第一個通配          - StripPrefix=1          #增加一個名稱為X-Request-Foo,值為Bar的請求頭          - AddRequestHeader=X-Request-Foo,Bar

這裡需要注意的是新增的這個請求頭是轉發以後添加進去的,所以我們請求網關的時候在瀏覽器中是找不到的,我們可以使用command+N(Windows中idea為Ctrl+N)來查找NettyRoutingFilter類,並且在filter方法中設置斷點,由以下圖中可以看到它是被添加進去了。

  • AddRequestParameter

由於在user模組中有這麼一個方法

@GetMapping("/query")  public String query(@RequestParam("name") String name) {      return name;  }

所以我們在網關配置時

spring:    application:      name: gateway    cloud:      nacos:        discovery:          server-addr: 127.0.0.1:8848      gateway:        routes:        - id: gate          uri: lb://user          predicates:          #由/user-center來匹配跳轉          - Path=/user-center/**          filters:          #跳轉後省略第一個通配          - StripPrefix=1          #增加一個名稱為name,值為locky的請求參數          - AddRequestParameter=name,locky

所以我們在網關中請求就可以不寫參數,直接訪問

  • AddResponseHeader
spring:    application:      name: gateway    cloud:      nacos:        discovery:          server-addr: 127.0.0.1:8848      gateway:        routes:        - id: gate          uri: lb://user          predicates:          #由/user-center來匹配跳轉          - Path=/user-center/**          filters:          #跳轉後省略第一個通配          - StripPrefix=1          #增加一個名稱為X-Response-Foo,值為Bar的響應頭          - AddResponseHeader=X-Response-Foo, Bar
  • DedupeResponseHeader

Spring Cloud Greenwich SR2提供的新特性,低於這個版本無法使用。 它的主要作用是去重,例如

spring:    application:      name: gateway    cloud:      nacos:        discovery:          server-addr: 127.0.0.1:8848      gateway:        routes:        - id: gate          uri: lb://user          predicates:          #由/user-center來匹配跳轉          - Path=/user-center/**          - Cookie=test,value          filters:          #跳轉後省略第一個通配          - StripPrefix=1          #在Http響應報文頭中進行去重,去重目標為跨域請求          - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin
  • Hystrix
Hystrix是Spring Cloud第一代中的容錯組件,不過已經進入維護模式。未來,Hystrix會被Spring Cloud移除掉,取而代之的是Alibaba Sentinel/Resilience4J。

此處不做具體設置了

  • FallbackHeaders

也是對Hystrix的支援,不做具體設置了

  • PrefixPath

為匹配的路由添加前綴,我們在user模組的find添加一層訪問路徑

@GetMapping("/test/find")  @SuppressWarnings("unchecked")  public Result<User> findStr() {      log.info("訪問成功");      return Result.success(new User(1,"張三",23));  }

網關配置

spring:    application:      name: gateway    cloud:      nacos:        discovery:          server-addr: 127.0.0.1:8848      gateway:        routes:        - id: gate          uri: lb://user          predicates:          #由/user-center來匹配跳轉          - Path=/user-center/**          filters:          #跳轉後省略第一個通配          - StripPrefix=1          #跳轉後添加前綴/test          - PrefixPath=/test

一致。

  • PreserveHostHeader

如果不設置,那麼名為 Host 的Header由Http Client控制;如果設置了,那麼會設置一個請求屬性(preserveHostHeader=true),路由過濾器會檢查從而去判斷是否要發送原始的、名為Host的Header。這裡主要是通過網關是否向代理伺服器轉發請求頭中的Host屬性。

spring:    application:      name: gateway    cloud:      nacos:        discovery:          server-addr: 127.0.0.1:8848      gateway:        routes:        - id: gate          uri: lb://user          predicates:          #由/user-center來匹配跳轉          - Path=/user-center/**          filters:          #跳轉後省略第一個通配          - StripPrefix=1          #轉發客戶端的請求報文頭Host給後端代理伺服器          - PreserveHostHeader
  • RequestRateLimiter

Gateway自帶的限流服務,但後續我們會整合Gateway和Sentinel來進行限流和熔斷。

  • RedirectTo

轉發到後端服務後再重定向到一個url.

spring:    application:      name: gateway    cloud:      nacos:        discovery:          server-addr: 127.0.0.1:8848      gateway:        routes:        - id: gate          uri: lb://user          predicates:          #由/user-center來匹配跳轉          - Path=/user-center/**          filters:          #跳轉後省略第一個通配          - StripPrefix=1          #轉發到Path,並且攜帶一個http://www.baidu.com到Location的響應頭          - RedirectTo=302,http://www.baidu.com

從以上圖中可以看出,其實我們請求的是http://127.0.0.1:8040/user-center/find,但是被重定向到了百度。這裡HTTP狀態碼應該是HTTP狀態碼300序列,例如301.302,具體狀態碼可以參考HTTP協議整理

  • RemoveHopByHopHeadersFilter

移除轉發請求的Header,多個用","分隔。默認情況下移除如下Header。

  1. Connection
  2. Keep-Alive
  3. Proxy-Authenticate
  4. Proxy-Authorization
  5. TE
  6. Trailer
  7. Transfer-Encoding
  8. Upgrade
spring:    application:      name: gateway    cloud:      nacos:        discovery:          server-addr: 127.0.0.1:8848      gateway:        routes:        - id: gate          uri: lb://user          predicates:          #由/user-center來匹配跳轉          - Path=/user-center/**          filters:          #跳轉後省略第一個通配          - StripPrefix=1        filter:          #移除轉發請求          remove-hop-by-hop:            headers: Keep-Alive,Connection
  • RemoveRequestHeader

移除原始請求頭

spring:    application:      name: gateway    cloud:      nacos:        discovery:          server-addr: 127.0.0.1:8848      gateway:        routes:        - id: gate          uri: lb://user          predicates:          #由/user-center來匹配跳轉          - Path=/user-center/**          filters:          #跳轉後省略第一個通配          - StripPrefix=1          #移除原始請求頭X-Request-Foo          - RemoveRequestHeader=X-Request-Foo

spring cloud zuul網關的作用 可知,在跨域轉發中,我們需要移除這些請求頭

spring:    application:      name: gateway    cloud:      nacos:        discovery:          server-addr: 127.0.0.1:8848      gateway:        routes:        - id: gate          uri: lb://user          predicates:          #由/user-center來匹配跳轉          - Path=/user-center/**          filters:          #跳轉後省略第一個通配          - StripPrefix=1          #移除原始跨域請求頭          - RemoveRequestHeader=Access-Control-Allow-Origin        filter:          #移除轉發請求          remove-hop-by-hop:            headers: Access-Control-Allow-Credentials,Access-Control-Allow-Origin,Vary,X-Frame-Options,token
  • RemoveResponseHeader

移除響應頭

我們在user中添加一個Controller方法

@GetMapping("/addhead")  public String addHeader(HttpServletRequest request, HttpServletResponse response) {      response.addHeader("X-Response-Foo","Foo");      return "header";  }

網關配置

spring:    application:      name: gateway    cloud:      nacos:        discovery:          server-addr: 127.0.0.1:8848      gateway:        routes:        - id: gate          uri: lb://user          predicates:          #由/user-center來匹配跳轉          - Path=/user-center/**          filters:          #跳轉後省略第一個通配          - StripPrefix=1          #移除響應頭X-Response-Foo          - RemoveResponseHeader=X-Response-Foo

通過網關轉發,我們可以看到無此X-Response-Foo的響應頭。

  • RewritePath

重寫請求路徑

spring:    application:      name: gateway    cloud:      nacos:        discovery:          server-addr: 127.0.0.1:8848      gateway:        routes:        - id: gate          uri: lb://user          predicates:          #由/user-center來匹配跳轉          - Path=/user-center/**          filters:          #配置成原始路徑正則, 重寫後的路徑的正則          - RewritePath=/user-center/(?<segment>.*), /${segment}

以上配置會將/user-center/find變成/find再轉發

直接訪問user

網關請求的

  • RewriteResponseHeader

重寫響應頭部分內容,根據正則來修改

之前在user中有一個Controller方法

@GetMapping("/addhead")  public String addHeader(HttpServletRequest request, HttpServletResponse response) {      response.addHeader("X-Response-Foo","Foo");      return "header";  }
spring:    application:      name: gateway    cloud:      nacos:        discovery:          server-addr: 127.0.0.1:8848      gateway:        routes:        - id: gate          uri: lb://user          predicates:          #由/user-center來匹配跳轉          - Path=/user-center/**          filters:          #跳轉後省略第一個通配          - StripPrefix=1          #重寫響應頭X-Response-Foo的值Foo為dee,內容可根據正則匹配          - RewriteResponseHeader=X-Response-Foo,Foo,dee

訪問user的/addhead,X-Response-Foo響應頭的值為Foo.

通過網關訪問/addhead,X-Response-Foo響應頭的值為dee

  • SaveSession