Spring Cloud——微服務(wù)網(wǎng)關(guān)Spring Cloud GateWay

Spring Cloud GateWay

Spring 自己開發(fā)的新一代API網(wǎng)關(guān)產(chǎn)品,基于NIO異步處理,摒棄了Zuul基于Servlet同步通信的設(shè)計(jì)。

Spring Cloud Gateway 作為 Spring Cloud 生態(tài)系統(tǒng)中的網(wǎng)關(guān),目標(biāo)是替代 Netflix Zuul,其不僅提供統(tǒng)一的路由方式,并且基于 Filter 鏈的方式提供了網(wǎng)關(guān)基本的功能,例如:安全,監(jiān)控/指標(biāo),和限流。

關(guān)鍵特征:

  • 1、基于JDK8+開發(fā)。
  • 2、Spring Cloud Gateway 是 Spring Cloud 的一個(gè)全新項(xiàng)目,該項(xiàng)目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技術(shù)開發(fā)的網(wǎng)關(guān),它旨在為微服務(wù)架構(gòu)提供一種簡單有效的統(tǒng)一的 API 路由管理方式。
  • 3、支持動(dòng)態(tài)路由,能夠匹配任何請求屬性上的路由。
  • 4、支持基于HTTP請求的路由匹配(Path、Method、Header、Host等)。
  • 5、過濾器可以修改HTTP請求和HTTP響應(yīng)。

在性能方面,根據(jù)官方提供的基準(zhǔn)測試, Spring Cloud Gateway 的 RPS(每秒請求數(shù))是Zuul 的 1.6 倍。

Spring Cloud Gateway十分優(yōu)秀,Spring Cloud Alibaba也默認(rèn)選用該組件作為網(wǎng)關(guān)產(chǎn)品。

相關(guān)概念:

  • Route(路由):這是網(wǎng)關(guān)的基本構(gòu)建塊。它由一個(gè) ID,一個(gè)目標(biāo) URI,一組斷言和一組過濾器定義。如果斷言為真,則路由匹配。
  • Predicate(斷言):這是一個(gè) Java 8 的 Predicate。輸入類型是一個(gè) ServerWebExchange。我們可以使用它來匹配來自 HTTP 請求的任何內(nèi)容,例如 headers 或參數(shù)。
  • Filter(過濾器):這是org.springframework.cloud.gateway.filter.GatewayFilter的實(shí)例,我們可以使用它修改請求和響應(yīng)。

工作流程:

客戶端向 Spring Cloud Gateway 發(fā)出請求。如果 Gateway Handler Mapping 中找到與請求相匹配的路由,將其發(fā)送到 Gateway Web Handler。Handler 再通過指定的過濾器鏈來將請求發(fā)送到我們實(shí)際的服務(wù)執(zhí)行業(yè)務(wù)邏輯,然后返回。 過濾器之間用虛線分開是因?yàn)檫^濾器可能會(huì)在發(fā)送代理請求之前(“pre”)或之后(“post”)執(zhí)行業(yè)務(wù)邏輯。

Spring Cloud Gateway 的特征:

  • 基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
  • 動(dòng)態(tài)路由
  • Predicates 和 Filters 作用于特定路由
  • 集成 Hystrix 斷路器
  • 集成 Spring Cloud DiscoveryClient
  • 易于編寫的 Predicates 和 Filters
  • 限流
  • 路徑重寫

Spring Cloud GateWay 快速上手

Spring Cloud Gateway 網(wǎng)關(guān)路由有兩種配置方式:

  • 在配置文件 yml 中配置
  • 通過@Bean自定義 RouteLocator,在啟動(dòng)主類 Application 中配置

這兩種方式是等價(jià)的,建議使用 yml 方式進(jìn)配置。

引入依賴:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.12.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<dependencies>
    <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-config</artifactId>
    </dependency>

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
</dependencies> 

<dependencyManagement>
    <dependencies>
        <!--整合Spring Cloud-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR12</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <!--整合Spring Cloud Alibaba-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-dependencies</artifactId>
            <version>2.2.6.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement> 

Spring Cloud Gateway 是使用 netty+webflux 實(shí)現(xiàn)因此不需要再引入 web 模塊。

gateWay的主要功能之一是轉(zhuǎn)發(fā)請求,轉(zhuǎn)發(fā)規(guī)則的定義主要包含三個(gè)部分:

  • Route(路由):路由是網(wǎng)關(guān)的基本單元,由ID、URI、一組Predicate、一組Filter組成,根據(jù)Predicate進(jìn)行匹配轉(zhuǎn)發(fā)。
  • Predicate(謂語、斷言):路由轉(zhuǎn)發(fā)的判斷條件,目前SpringCloud Gateway支持多種方式,常見如:Path、Query、Method、Header等,寫法必須遵循 key=vlue的形式。
  • Filter(過濾器):過濾器是路由轉(zhuǎn)發(fā)請求時(shí)所經(jīng)過的過濾邏輯,可用于修改請求、響應(yīng)內(nèi)容。

注意:其中Route和Predicate必須同時(shí)申明

路由配置方式

基礎(chǔ)URI路由配置方式

  • 如果請求的目標(biāo)地址,是單個(gè)的URI資源路徑,配置文件示例如下:
//通過配置文件配置
spring:
  cloud:
    gateway:
      routes:
        - id: gate_route
          uri: http://localhost:9023
          predicates:
          ## 當(dāng)請求的路徑為gate、rule開頭的時(shí),轉(zhuǎn)發(fā)到http://localhost:9023服務(wù)器上
            - Path=/gate/**,/rule/**
        ### 請求路徑前加上/app
          filters:
          - PrefixPath=/app     

基于代碼的路由配置方式

轉(zhuǎn)發(fā)功能同樣可以通過代碼來實(shí)現(xiàn),我們可以在啟動(dòng)類 GateWayApplication 中添加方法 customRouteLocator() 來定制轉(zhuǎn)發(fā)規(guī)則。

@SpringBootApplication
public class GatewayApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
 
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("path_route", r -> r.path("/csdn")
                    .uri("https://blog.csdn.net"))
                .build();
    }
}

上面配置了一個(gè) id 為 path_route 的路由,當(dāng)訪問地址http://localhost:8080/about時(shí)會(huì)自動(dòng)轉(zhuǎn)發(fā)到地址:https://blog.csdn.net/csdn和上面的轉(zhuǎn)發(fā)效果一樣,只是這里轉(zhuǎn)發(fā)的是以項(xiàng)目地址/csdn格式的請求地址。

和注冊中心相結(jié)合的路由配置方式

在uri的schema協(xié)議部分為自定義的lb:類型,表示從微服務(wù)注冊中心(如Eureka)訂閱服務(wù),并且進(jìn)行服務(wù)的路由。

server:
  port: 8084
spring:
  cloud:
    gateway:
      routes:
      -id: seckill-provider-route
        uri: lb://seckill-provider
        predicates:
        - Path=/seckill-provider/**

      -id: message-provider-route
        uri: lb://message-provider
        predicates:
        -Path=/message-provider/**

application:
  name: cloud-gateway

eureka:
  instance:
    prefer-ip-address: true
  client:
    service-url:
      defaultZone: http://localhost:8888/eureka/

注冊中心相結(jié)合的路由配置方式,與單個(gè)URI的路由配置,區(qū)別其實(shí)很小,僅僅在于URI的schema協(xié)議不同。單個(gè)URI的地址的schema協(xié)議,一般為http或者h(yuǎn)ttps協(xié)議。

路由匹配規(guī)則

Spring Cloud Gateway 的功能很強(qiáng)大,我們僅僅通過 Predicates 的設(shè)計(jì)就可以看出來,前面我們只是使用了 predicates 進(jìn)行了簡單的條件匹配,其實(shí) Spring Cloud Gataway 幫我們內(nèi)置了很多 Predicates 功能。

Spring Cloud Gateway 是通過 Spring WebFlux 的 HandlerMapping 做為底層支持來匹配到轉(zhuǎn)發(fā)路由,Spring Cloud Gateway 內(nèi)置了很多 Predicates 工廠,這些 Predicates 工廠通過不同的 HTTP 請求參數(shù)來匹配,多個(gè) Predicates 工廠可以組合使用。

Predicate 斷言條件(轉(zhuǎn)發(fā)規(guī)則)介紹

Predicate 來源于 Java 8,是 Java 8 中引入的一個(gè)函數(shù),Predicate 接受一個(gè)輸入?yún)?shù),返回一個(gè)布爾值結(jié)果。該接口包含多種默認(rèn)方法來將 Predicate 組合成其他復(fù)雜的邏輯(比如:與,或,非)。可以用于接口請求參數(shù)校驗(yàn)、判斷新老數(shù)據(jù)是否有變化需要進(jìn)行更新操作。

在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性實(shí)現(xiàn)了各種路由匹配規(guī)則,有通過 Header、請求參數(shù)等不同的條件來進(jìn)行作為條件匹配到對應(yīng)的路由。網(wǎng)上有一張圖總結(jié)了 Spring Cloud 內(nèi)置的幾種 Predicate 的實(shí)現(xiàn)。

說白了 Predicate 就是為了實(shí)現(xiàn)一組匹配規(guī)則,方便讓請求過來找到對應(yīng)的 Route 進(jìn)行處理,接下來我們接下 Spring Cloud GateWay 內(nèi)置幾種 Predicate 的使用。

  • 轉(zhuǎn)發(fā)規(guī)則(predicates),假設(shè) 轉(zhuǎn)發(fā)uri都設(shè)定為http://localhost:9023
規(guī)則 實(shí)例 說明
Path - Path=/gate/,/rule/ 當(dāng)請求的路徑為gate、rule開頭的時(shí),轉(zhuǎn)發(fā)到http://localhost:9023服務(wù)器上
Before - Before=2017-01-20T17:42:47.789-07:00[America/Denver] 在某個(gè)時(shí)間之前的請求才會(huì)被轉(zhuǎn)發(fā)到 http://localhost:9023服務(wù)器上
After - After=2017-01-20T17:42:47.789-07:00[America/Denver] 在某個(gè)時(shí)間之后的請求才會(huì)被轉(zhuǎn)發(fā)
Between - Between=2017-01-20T17:42:47.789-07:00[America/Denver],2017-01-21T17:42:47.789-07:00[America/Denver] 在某個(gè)時(shí)間段之間的才會(huì)被轉(zhuǎn)發(fā)
Cookie - Cookie=chocolate, ch.p 名為chocolate的表單或者滿足正則ch.p的表單才會(huì)被匹配到進(jìn)行請求轉(zhuǎn)發(fā)
Header - Header=X-Request-Id, \d+ 攜帶參數(shù)X-Request-Id或者滿足\d+的請求頭才會(huì)匹配
Host - Host=www.hd123.com 當(dāng)主機(jī)名為www.hd123.com的時(shí)候直接轉(zhuǎn)發(fā)到http://localhost:9023服務(wù)器上
Method - Method=GET 只有GET方法才會(huì)匹配轉(zhuǎn)發(fā)請求,還可以限定POST、PUT等請求方式

通過請求參數(shù)匹配

  • Query Route Predicate 支持傳入兩個(gè)參數(shù),一個(gè)是屬性名一個(gè)為屬性值,屬性值可以是正則表達(dá)式。
server:
  port: 8080
spring:
  application:
     name: api-gateway
  cloud:
    gateway:
      routes:
        - id: gateway-service
          uri: https://www.baidu.com
          order: 0
          predicates:
            - Query=smile

只要請求中包含 smile 屬性的參數(shù)即可匹配路由,不帶 smile 參數(shù)則不會(huì)匹配。

curl localhost:8080?smile=x&id=2
  • 還可以將 Query 的值以鍵值對的方式進(jìn)行配置,這樣在請求過來時(shí)會(huì)對屬性值和正則進(jìn)行匹配,匹配上才會(huì)走路由。
server:
  port: 8080
spring:
  application:
     name: api-gateway
  cloud:
    gateway:
      routes:
        - id: gateway-service
          uri: https://www.baidu.com
          order: 0
          predicates:
            - Query=keep, pu.

這樣只要當(dāng)請求中包含 keep 屬性并且參數(shù)值是以 pu 開頭的長度為三位的字符串才會(huì)進(jìn)行匹配和路由。

curl localhost:8080?keep=pub

通過 Header 屬性匹配

Header Route Predicate 和 Cookie Route Predicate 一樣,也是接收 2 個(gè)參數(shù),一個(gè) header 中屬性名稱和一個(gè)正則表達(dá)式,這個(gè)屬性值和正則表達(dá)式匹配則執(zhí)行。

server:
  port: 8080
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: gateway-service
          uri: https://www.baidu.com
          order: 0
          predicates:
            - Header=X-Request-Id, \d+

使用 curl 測試,命令行輸入:

curl http://localhost:8080 -H "X-Request-Id:88"

則返回頁面代碼證明匹配成功。將參數(shù)-H "X-Request-Id:88"改為-H "X-Request-Id:spring"再次執(zhí)行時(shí)返回404證明沒有匹配。

通過 Cookie 匹配

Cookie Route Predicate 可以接收兩個(gè)參數(shù),一個(gè)是 Cookie name ,一個(gè)是正則表達(dá)式,路由規(guī)則會(huì)通過獲取對應(yīng)的 Cookie name 值和正則表達(dá)式去匹配,如果匹配上就會(huì)執(zhí)行路由,如果沒有匹配上則不執(zhí)行。

server:
  port: 8080
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: gateway-service
          uri: https://www.baidu.com
          order: 0
          predicates:
            - Cookie=sessionId, test

使用 curl 測試,命令行輸入:

curl http://localhost:8080 --cookie "sessionId=test"

則會(huì)返回頁面代碼,如果去掉--cookie "sessionId=test",后臺(tái)匯報(bào) 404 錯(cuò)誤。

通過 Host 匹配

Host Route Predicate 接收一組參數(shù),一組匹配的域名列表,這個(gè)模板是一個(gè) ant 分隔的模板,用.號作為分隔符。它通過參數(shù)中的主機(jī)地址作為匹配規(guī)則。

server:
  port: 8080
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: gateway-service
          uri: https://www.baidu.com
          order: 0
          predicates:
            - Host=**.baidu.com

使用 curl 測試,命令行輸入:

curl http://localhost:8080 -H "Host: www.baidu.com"

curl http://localhost:8080 -H "Host: md.baidu.com"

經(jīng)測試以上兩種 host 均可匹配到 host_route 路由,去掉 host 參數(shù)則會(huì)報(bào) 404 錯(cuò)誤。

通過請求方式匹配

可以通過是 POST、GET、PUT、DELETE 等不同的請求方式來進(jìn)行路由。

server:
  port: 8080
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: gateway-service
          uri: https://www.baidu.com
          order: 0
          predicates:
            - Method=GET

使用 curl 測試,命令行輸入:

# curl 默認(rèn)是以 GET 的方式去請求

curl http://localhost:8080

測試返回頁面代碼,證明匹配到路由,我們再以 POST 的方式請求測試。

# curl 默認(rèn)是以 GET 的方式去請求

curl -X POST http://localhost:8080

返回 404 沒有找到,證明沒有匹配上路由。

通過請求路徑匹配

Path Route Predicate 接收一個(gè)匹配路徑的參數(shù)來判斷是否走路由。

server:
  port: 8080
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: gateway-service
          uri: http://ityouknow.com
          order: 0
          predicates:
            - Path=/foo/{segment}

如果請求路徑符合要求,則此路由將匹配,例如:/foo/1 或者 /foo/bar。

使用 curl 測試,命令行輸入:

curl http://localhost:8080/foo/1

curl http://localhost:8080/foo/xx

curl http://localhost:8080/boo/xx

經(jīng)過測試第一和第二條命令可以正常獲取到頁面返回值,最后一個(gè)命令報(bào)404,證明路由是通過指定路由來匹配。

通過請求 ip 地址進(jìn)行匹配

Predicate 也支持通過設(shè)置某個(gè) ip 區(qū)間號段的請求才會(huì)路由,RemoteAddr Route Predicate 接受 cidr 符號(IPv4 或 IPv6 )字符串的列表(最小大小為1),例如 192.168.0.1/16 (其中 192.168.0.1 是 IP 地址,16 是子網(wǎng)掩碼)。

server:
  port: 8080
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: gateway-service
          uri: https://www.baidu.com
          order: 0
          predicates:
            - RemoteAddr=192.168.1.1/24

可以將此地址設(shè)置為本機(jī)的 ip 地址進(jìn)行測試。

curl localhost:8080

如果請求的遠(yuǎn)程地址是 192.168.1.10,則此路由將匹配。

組合使用

server:
  port: 8080
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: gateway-service
          uri: https://www.baidu.com
          order: 0
          predicates:
            - Host=**.foo.org
            - Path=/headers
            - Method=GET
            - Header=X-Request-Id, \d+
            - Query=foo, ba.
            - Query=baz
            - Cookie=chocolate, ch.p

各種 Predicates 同時(shí)存在于同一個(gè)路由時(shí),請求必須同時(shí)滿足所有的條件才被這個(gè)路由匹配。

一個(gè)請求滿足多個(gè)路由的斷言條件時(shí),請求只會(huì)被首個(gè)成功匹配的路由轉(zhuǎn)發(fā)。

過濾器規(guī)則(Filter)

過濾規(guī)則 實(shí)例 說明
PrefixPath - PrefixPath=/app 在請求路徑前加上app
RewritePath - RewritePath=/test, /app/test 訪問localhost:9022/test,請求會(huì)轉(zhuǎn)發(fā)到localhost:8001/app/test
SetPath SetPath=/app/{path} 通過模板設(shè)置路徑,轉(zhuǎn)發(fā)的規(guī)則時(shí)會(huì)在路徑前增加app,{path}表示原請求路徑
RedirectTo 重定向
RemoveRequestHeader 去掉某個(gè)請求頭信息

注意:當(dāng)配置多個(gè)filter時(shí),優(yōu)先定義的會(huì)被調(diào)用,剩余的filter將不會(huì)生效

PrefixPath

  • 對所有的請求路徑添加前綴:
spring:
  cloud:
    gateway:
      routes:
        - id: prefixpath_route
          uri: https://example.org
          filters:
            - PrefixPath=/mypath

訪問/hello的請求被發(fā)送到https://example.org/mypath/hello

RedirectTo

  • 重定向,配置包含重定向的返回碼和地址:
spring:
  cloud:
    gateway:
      routes:
        - id: prefixpath_route
          uri: https://example.org
          filters:
            - RedirectTo=302, https://acme.org

RemoveRequestHeader

  • 去掉某個(gè)請求頭信息:
spring:
  cloud:
    gateway:
      routes:
        - id: removerequestheader_route
          uri: https://example.org
          filters:
            - RemoveRequestHeader=X-Request-Foo

去掉請求頭信息 X-Request-Foo

RemoveResponseHeader

  • 去掉某個(gè)回執(zhí)頭信息:
spring:
  cloud:
    gateway:
      routes:
        - id: removerequestheader_route
          uri: https://example.org
          filters:
            - RemoveResponseHeader=X-Request-Foo

RemoveRequestParameter

  • 去掉某個(gè)請求參數(shù)信息:
spring:
  cloud:
    gateway:
      routes:
        - id: removerequestparameter_route
          uri: https://example.org
          filters:
            - RemoveRequestParameter=red

RewritePath

  • 改寫路徑:
spring:
  cloud:
    gateway:
      routes:
        - id: rewrite_filter
          uri: http://localhost:8081
          predicates:
            - Path=/test/**
          filters:
            - RewritePath=/where(?<segment>/?.*), /test(?<segment>/?.*)

/where/... 改成 test/...

使用代碼改下路徑

RouteLocatorBuilder.Builder builder = routeLocatorBuilder.routes();
builder.route("path_rote_at_guigu", r -> r.path("/guonei")
                .uri("http://news.baidu.com/guonei"))
        .route("csdn_route", r -> r.path("/csdn")
                .uri("https://blog.csdn.net"))
        .route("blog3_rewrite_filter", r -> r.path("/blog3/**")
                .filters(f -> f.rewritePath("/blog3/(?<segment>.*)", "/$\\{segment}"))
                .uri("https://blog.csdn.net"))
        .route("rewritepath_route", r -> r.path("/baidu/**")
                .filters(f -> f.rewritePath("/baidu/(?<segment>.*)", "/$\\{segment}"))
                .uri("http://www.baidu.com"))
        .build();

SetPath

  • 設(shè)置請求路徑,與RewritePath類似。
spring:
  cloud:
    gateway:
      routes:
        - id: setpath_route
          uri: https://example.org
          predicates:
            - Path=/red/{segment}
          filters:
            - SetPath=/{segment}

如/red/blue的請求被轉(zhuǎn)發(fā)到/blue。

SetRequestHeader

  • 設(shè)置請求頭信息。
spring:
  cloud:
    gateway:
      routes:
        - id: setrequestheader_route
          uri: https://example.org
          filters:
            - SetRequestHeader=X-Request-Red, Blue

SetStatus

  • 設(shè)置回執(zhí)狀態(tài)碼。
spring:
  cloud:
    gateway:
      routes:
        - id: setstatusint_route
          uri: https://example.org
          filters:
            - SetStatus=401

StripPrefix

  • 跳過指定路徑。
spring:
  cloud:
    gateway:
      routes:
        - id: nameRoot
          uri: https://nameservice
          predicates:
            - Path=/name/**
          filters:
            - StripPrefix=2

請求/name/blue/red會(huì)轉(zhuǎn)發(fā)到/red。

RequestSize

  • 請求大小。
spring:
  cloud:
    gateway:
      routes:
        - id: request_size_route
          uri: http://localhost:8080/upload
          predicates:
            - Path=/upload
          filters:
            - name: RequestSize
              args:
                maxSize: 5000000

超過5M的請求會(huì)返回413錯(cuò)誤。

Default-filters

  • 對所有請求添加過濾器。
spring:
  cloud:
    gateway:
      default-filters:
        - AddResponseHeader=X-Response-Default-Red, Default-Blue
        - PrefixPath=/httpbin

通過代碼進(jìn)行配置

  • 通過代碼進(jìn)行配置,將路由規(guī)則設(shè)置為一個(gè)Bean即可:
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("path_route", r -> r.path("/get")
            .uri("http://httpbin.org"))
        .route("host_route", r -> r.host("*.myhost.org")
            .uri("http://httpbin.org"))
        .route("rewrite_route", r -> r.host("*.rewrite.org")
            .filters(f -> f.rewritePath("/foo/(?<segment>.*)", "/${segment}"))
            .uri("http://httpbin.org"))
        .route("hystrix_route", r -> r.host("*.hystrix.org")
            .filters(f -> f.hystrix(c -> c.setName("slowcmd")))
            .uri("http://httpbin.org"))
        .route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org")
            .filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback")))
            .uri("http://httpbin.org"))
        .route("limit_route", r -> r
            .host("*.limited.org").and().path("/anything/**")
            .filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))
            .uri("http://httpbin.org"))
        .build();
}

統(tǒng)一配置跨域請求:

現(xiàn)在的請求通過經(jīng)過gateWay網(wǎng)關(guān)時(shí),需要在網(wǎng)關(guān)統(tǒng)一配置跨域請求,需求所有請求通過

spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowed-origins: "*"
            allowed-headers: "*"
            allow-credentials: true
            allowed-methods:
              - GET
              - POST
              - DELETE
              - PUT
              - OPTION

Gatway 網(wǎng)關(guān)的過濾器開發(fā)

過濾器的執(zhí)行次序

Spring-Cloud-Gateway 基于過濾器實(shí)現(xiàn),同 zuul 類似,有pre和post兩種方式的 filter,分別處理前置邏輯和后置邏輯??蛻舳说恼埱笙冉?jīng)過pre類型的 filter,然后將請求轉(zhuǎn)發(fā)到具體的業(yè)務(wù)服務(wù),收到業(yè)務(wù)服務(wù)的響應(yīng)之后,再經(jīng)過post類型的 filter 處理,最后返回響應(yīng)到客戶端。

過濾器執(zhí)行流程如下,order 越大,優(yōu)先級越低


Spring Cloud Gateway根據(jù)作用范圍劃分為GatewayFilter和GlobalFilter,二者區(qū)別如下:

  • GatewayFilter:需要通過spring.cloud.routes.filters 配置在具體路由下,只作用在當(dāng)前路由上或通過spring.cloud.default-filters配置在全局,作用在所有路由上

  • GlobalFilter:全局過濾器,不需要在配置文件中配置,作用在所有的路由上,最終通過GatewayFilterAdapter包裝成GatewayFilterChain可識(shí)別的過濾器,它為請求業(yè)務(wù)以及路由的URI轉(zhuǎn)換為真實(shí)業(yè)務(wù)服務(wù)的請求地址的核心過濾器,不需要配置,系統(tǒng)初始化時(shí)加載,并作用在每個(gè)路由上。

Spring Cloud Gateway框架內(nèi)置的GlobalFilter如下:


定義全局過濾器

實(shí)現(xiàn) GlobalFilter 和 Ordered,重寫相關(guān)方法,加入到spring容器管理即可,無需配置,全局過濾器對所有的路由都有效。

全局過濾器舉例:代碼如下:

@Configuration
public class FilterConfig {

    @Bean
    @Order(-1)
    public GlobalFilter a() {
        return new AFilter();
    }

    @Bean
    @Order(0)
    public GlobalFilter b() {
        return new BFilter();
    }

    @Bean
    @Order(1)
    public GlobalFilter c() {
        return new CFilter();
    }


    @Slf4j
    public class AFilter implements GlobalFilter, Ordered {

        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            log.info("AFilter前置邏輯");
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                log.info("AFilter后置邏輯");
            }));
        }

        //值越小,優(yōu)先級越高
        //int HIGHEST_PRECEDENCE = -2147483648;
        //int LOWEST_PRECEDENCE = 2147483647;
        @Override
        public int getOrder() {
            return HIGHEST_PRECEDENCE + 100;
        }
    }

    @Slf4j
    public class BFilter implements GlobalFilter, Ordered {
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            log.info("BFilter前置邏輯");
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                log.info("BFilter后置邏輯");
            }));
        }

        //值越小,優(yōu)先級越高
        //int HIGHEST_PRECEDENCE = -2147483648;
        //int LOWEST_PRECEDENCE = 2147483647;
        @Override
        public int getOrder() {
            return HIGHEST_PRECEDENCE + 200;
        }
    }

    @Slf4j
    public class CFilter implements GlobalFilter, Ordered {

        @Override 
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            log.info("CFilter前置邏輯");
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                log.info("CFilter后置邏輯");
            }));
        }

        //值越小,優(yōu)先級越高
        //int HIGHEST_PRECEDENCE = -2147483648;
        //int LOWEST_PRECEDENCE = 2147483647;
        @Override
        public int getOrder() {
            return HIGHEST_PRECEDENCE + 300;
        }
    }
}

定義局部過濾器

步驟:

  • 1、需要實(shí)現(xiàn)GatewayFilter, Ordered,實(shí)現(xiàn)相關(guān)的方法
  • 2、加入到過濾器工廠,并且注冊到spring容器中。
  • 3、在配置文件中進(jìn)行配置,如果不配置則不啟用此過濾器規(guī)則。

局部過濾器舉例, 對請求頭部的 user-id 進(jìn)行校驗(yàn),代碼如下:

  • 1、需要實(shí)現(xiàn)GatewayFilter, Ordered,實(shí)現(xiàn)相關(guān)的方法
@Slf4j
public class UserIdCheckGateWayFilter implements GatewayFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String url = exchange.getRequest().getPath().pathWithinApplication().value();
        log.info("請求URL:" + url);
        log.info("method:" + exchange.getRequest().getMethod());
        //獲取param 請求參數(shù)
        String uname = exchange.getRequest().getQueryParams().getFirst("uname");
        //獲取header
        String userId = exchange.getRequest().getHeaders().getFirst("user-id");
        log.info("userId:" + userId);

        if (StringUtils.isEmpty(userId)) {
            log.info("*****頭部驗(yàn)證不通過,請?jiān)陬^部輸入  user-id");
            //終止請求,直接回應(yīng)
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    // 值越小,優(yōu)先級越高
    //int HIGHEST_PRECEDENCE = -2147483648;
    //int LOWEST_PRECEDENCE = 2147483647;
    @Override
    public int getOrder() {
        return HIGHEST_PRECEDENCE;
    }
}
  • 2、加入到過濾器工廠,并且注冊到spring容器中。
@Component
public class UserIdCheckGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {
    
    @Override
    public GatewayFilter apply(Object config) {
        return new UserIdCheckGateWayFilter();
    }
}
  • 3、在配置文件中進(jìn)行配置,如果不配置則不啟用此過濾器規(guī)則。
- id: service_provider_demo_route_filter
  uri: lb://service-provider-demo
  predicates:
    - Path=/filter/**
  filters:
    - RewritePath=/filter/(?<segment>.*), /provider/$\{segment}
    - UserIdCheck

參考:
http://www.ityouknow.com/springcloud/2018/12/12/spring-cloud-gateway-start.html

http://www.likecs.com/show-50293.html

https://zhuanlan.zhihu.com/p/299608850?utm_source=wechat_session

https://juejin.cn/post/6844903965352525838

https://blog.csdn.net/weixin_38361347/article/details/114108368

http://www.zyiz.net/tech/detail-98256.html

https://www.cnblogs.com/crazymakercircle/p/11704077.html

https://blog.csdn.net/forezp/article/details/85057268

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

相關(guān)閱讀更多精彩內(nèi)容

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