App下載

SpringCloud Gateway 2020.0.2最新版本的講解

猿友 2021-08-05 14:27:32 瀏覽數(shù) (4258)
反饋

 SpringCloud Gateway 作為 SpringCloud 中一個(gè)全新項(xiàng)目,目的是為了取代 Zuul 網(wǎng)關(guān)。本篇文章,將為大家介紹SpringCloud Gateway 2020.0.2 的最新版本的內(nèi)容。

簡述

官網(wǎng):https://spring.io/projects/spring-cloud-gateway
GitHub地址:https://github.com/spring-cloud/spring-cloud-gateway
本文編寫自2021年4月7日,當(dāng)前SpringCloud最新版本為2020.0.2版本
本文使用版本為
SpringCloud 版本2020.0.2
spring-cloud-starter-gateway版本3.0.2
spring-boot-starter版本2.4.4

該項(xiàng)目提供了一個(gè)用于在Spring WebFlux之上構(gòu)建API網(wǎng)關(guān)的庫。 Spring Cloud Gateway旨在提供一種簡單而有效的方法來路由到API,并為它們提供跨領(lǐng)域的關(guān)注點(diǎn),例如:安全性,監(jiān)視/指標(biāo)和彈性。

特征

  • 建立在Spring Framework 5,Project Reactor和Spring Boot 2.0之上
  • 能夠匹配任何請求屬性上的路由。
  • 謂詞和過濾器特定于路由。
  • 斷路器集成。
  • Spring Cloud DiscoveryClient集成
  • 易于編寫的謂詞和過濾器
  • 請求速率限制
  • 路徑改寫

概念

什么是路由?
路由是構(gòu)建網(wǎng)關(guān)的基本模塊,由ID,目標(biāo)URI,一系列的斷言和過濾器組成,如果斷言為true,則匹配該路由
什么是斷言?
開發(fā)人員可以匹配HTTP請求中的所有內(nèi)容(例如請求頭或請求參數(shù)),如果請求與斷言相匹配則進(jìn)行路由
什么是過濾?
值得是Spring框架中GatewayFilter的實(shí)例,使用過濾器,可以使請求在被路由前/后進(jìn)行修改

然后讓我們先通過幾個(gè)小demo先了解一下gateway的大概使用,然后我們在深入了解更多相關(guān)知識(shí),這樣比較容易理解。

獨(dú)立版代碼(靜態(tài)路由)

依賴坐標(biāo)

pom.xml

   <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>2.4.4</version>
        </dependency>
				<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
          	<version>3.0.2</version>
        </dependency>

配置

配置文件、配置類選擇一個(gè)就ok
根據(jù)此配置,訪問 http:localhost:9527/s 將直接轉(zhuǎn)發(fā)到百度首頁,頁面內(nèi)容展示百度首頁內(nèi)容
application.yml

server:
  port: 9527
spring:
  application:
    name: cloud-gateway
  # 以下是gateway的配置
  cloud:
    gateway:
      routes:
        - id: gateway_route_1
          uri: https://www.baidu.com/s
          predicates:
            - Path=/s/**

GatewayConfig.java org.example.springcloud.config.GatewayConfig

package org.example.springcloud.config;

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @Auth: guohui.wang
 * @Date: 2021-04-07 11:07
 * @desc:
 */
@Configuration
public class GatewayConfig {
    /**
     * <p>將 "ip:${server.port}+${route.path}" 轉(zhuǎn)發(fā)到 "${route.url}"</p>
     * <p>例 "120.0.0.1:9527/s" 轉(zhuǎn)發(fā)到 "https://www.baidu.com/s"</p><br>
     * <p>可以嘗試訪問以下鏈接獲取配置效果</p>
     * <li>"http://localhost:9527/s"</li>
     * <li>"http://localhost:9527/s?wd=spring"</li>
     * <br>
     * <div>
     * <b>routes().route(參數(shù)1,參數(shù)2).route(參數(shù)1,參數(shù)2)……</b>
     * <li>參數(shù)1:id,一個(gè)可以隨便取得名字,標(biāo)識(shí)此路由規(guī)則</li>
     * <li>參數(shù)2:具體的路由規(guī)則內(nèi)容</li>
     *
     * <b>路由規(guī)則:</b>
     * <li>path: 網(wǎng)關(guān)服務(wù)地址之后的內(nèi)容(比如上面的"/s")</li>
     * <li>uri: 符合path之后需要轉(zhuǎn)發(fā)到的位置</li>
     * </div>
     *
     * @param builder
     * @return
     */
    @Bean
    public RouteLocator routes(RouteLocatorBuilder builder) {
        return builder
                .routes()
                .route("gateway_route_1",
                        r -> r.path("/s")
                                .uri("https://www.baidu.com/s")
                )
                .build();
    }
}

啟動(dòng)類

org.example.springcloud.GatewayMain9527

package org.example.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class GatewayMain9527 {
    public static void main(String[] args) {
        SpringApplication.run(GatewayMain9527.class, args);
    }
}

與erueka整合版(動(dòng)態(tài)路由)

通過微服務(wù)名實(shí)現(xiàn)動(dòng)態(tài)路由,這樣可以在服務(wù)中心注冊的對(duì)應(yīng)的多個(gè)服務(wù)之間來回調(diào)用了。
需要三個(gè)微服務(wù),服務(wù)結(jié)構(gòu):

父項(xiàng)目

  • eureka服務(wù),作為注冊中心
  • gateway服務(wù),作為網(wǎng)關(guān)
  • provider服務(wù),作為網(wǎng)關(guān)轉(zhuǎn)發(fā)的服務(wù)

父項(xiàng)目

創(chuàng)建項(xiàng)目,選擇maven(個(gè)人喜好也可選擇SpringInitializr)->輸入名稱及相關(guān)信息(我這里起的名字是 springcloudgateway)->點(diǎn)擊finish創(chuàng)建項(xiàng)目

image.pngimage.png

修改pom,pom中添加版本控制

<dependencyManagement>
        <dependencies>
            <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-dependencies -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.4.4</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-dependencies -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>2020.0.2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

eureka模塊

創(chuàng)建項(xiàng)目,選擇maven(個(gè)人喜好也可選擇SpringInitializr)->輸入相關(guān)信息->Finish

我這里用的模塊名:cloud-eureka

image.pngimage.png

pom中添加坐標(biāo)依賴

 <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>

創(chuàng)建類EurekaApplication org.example.springcloud.eureka.EurekaApplication

package org.example.springcloud.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaApplication.class, args);
    }
}

在main/resources下面創(chuàng)建application.yml 并添加配置

server:
  port: 9001
eureka:
  instance:
    hostname: localhost
  client:
    # 不向注冊中心注冊自己
    register-with-eureka: false
    # 表示自己端就是注冊中心,不需要檢索服務(wù)
    fetch-registry: false
    service-url:
      # 設(shè)置與eureka server交互地址查詢服務(wù)和注冊服務(wù)都需要依賴這個(gè)地址
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/

驗(yàn)證

啟動(dòng)服務(wù)沒有問題(控制臺(tái)內(nèi)容略),并且訪問 http://localhost:9001/ 可以打開頁面:

image.png

provider模塊

這個(gè)模塊假裝作為提供服務(wù)的微服務(wù),用來測試網(wǎng)關(guān)是否生效的。

創(chuàng)建項(xiàng)目,選擇maven(個(gè)人喜好也可選擇SpringInitializr)->輸入相關(guān)信息->Finish

我這里用的模塊名:cloud-provider

image.pngimage.png

pom中添加坐標(biāo)依賴

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

創(chuàng)建類ProviderApplication org.example.springcloud.provider.ProviderApplication

package org.example.springcloud.provider;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class ProviderApplication {
    public static void main(String[] args) {
        SpringApplication.run(ProviderApplication.class, args);
    }
}

創(chuàng)建controller層類TestController org.example.springcloud.provider.controller.TestController

package org.example.springcloud.provider.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RequestMapping("/test")
@RestController
public class TestController {
    @GetMapping("/lb")
    public String lb() {
        return "訪問成功";
    }
}

在main/resources下面創(chuàng)建application.yml 并添加配置

server:
  port: 9003
spring:
  application:
    name: cloud-provider
#eureka相關(guān)配置
eureka:
  instance:
    instance-id: cloud-provider-01
  client:
    service-url:
      defaultZone: http://127.0.0.1:9001/eureka
    register-with-eureka: true
    fetch-registry: true

驗(yàn)證

服務(wù)啟動(dòng)沒問題,并且訪問 http://localhost:9003/test/lb 可以獲取到返回內(nèi)容,說明接口沒問題。

image.png

并且打開eureka頁面 http://localhost:9001/ 可以看到服務(wù)已經(jīng)注冊進(jìn)去,說明服務(wù)注冊也沒問題。

image.png

gateway模塊 創(chuàng)建項(xiàng)目,選擇maven(個(gè)人喜好也可選擇SpringInitializr)->輸入相關(guān)信息->Finish

我這里用的模塊名:cloud-gateway

image.pngimage.png

pom中添加坐標(biāo)依賴

 <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
    </dependencies>

創(chuàng)建類GatewayApplication org.example.springcloud.gateway.GatewayApplication

package org.example.springcloud.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class GatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}

在main/resources下面創(chuàng)建application.yml 并添加配置

server:
  port: 9002
spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          #開啟從注冊中心動(dòng)態(tài)創(chuàng)建路由功能,利用微服務(wù)名進(jìn)行路由
          enabled: true
      routes:
        - id: payment_routh2
          # lb:// + ${目標(biāo)服務(wù)的spring.application.name的值}
          uri: lb://cloud-provider
          # 映射的路徑 訪問符合Path值的路徑,轉(zhuǎn)發(fā)到響應(yīng)的uri對(duì)應(yīng)服務(wù)下Path
          predicates:
            - Path=/test/lb/**
#eureka相關(guān)配置
eureka:
  instance:
    instance-id: cloud-gateway-01
  client:
    service-url:
      defaultZone: http://127.0.0.1:9001/eureka
    register-with-eureka: true
    fetch-registry: true

驗(yàn)證

并且打開eureka頁面 http://localhost:9001/ 可以看到服務(wù)已經(jīng)注冊進(jìn)去,說明服務(wù)注冊沒問題。

image.png

根據(jù)配置訪問 http://localhost:9002/test/lb 如果出現(xiàn)正常返回頁面,則說明網(wǎng)關(guān)配置成功。

image.png

但此時(shí)應(yīng)該是訪問失敗的,會(huì)出現(xiàn)如下頁面,并伴隨控制臺(tái)的如下報(bào)錯(cuò):

image.png
image.png

看,報(bào)錯(cuò)內(nèi)容找我們的主機(jī)名了,java.net.UnknownHostException: failed to resolve 'WRGHO-VIEZQZWFI' after 4 queries 中的 ‘WRGHO-VIEZQZWFI' 就是我的主機(jī)名
不要著急,這時(shí)候我們往目標(biāo)服務(wù)(也就是provider服務(wù))中添加這樣一條配置就ok了:

image.png

此配置項(xiàng)表示:表示在猜測主機(jī)名時(shí),應(yīng)優(yōu)先使用服務(wù)器的IP地址,而不要使用OS報(bào)告的主機(jī)名

然后重啟provider服務(wù),重新訪問,如果訪問的時(shí)候報(bào)錯(cuò)503,看gateway控制臺(tái)內(nèi)容提示
No servers available for service: cloud-provider,說明服務(wù)雖然重啟了但是還么有注冊到服務(wù)中心里,我們等一小會(huì)等待注冊進(jìn)去再嘗試就ok了。
錯(cuò)誤頁面和控制臺(tái)提示內(nèi)容:

image.png
image.png

至此我們應(yīng)該對(duì)gateway的使用方法有個(gè)大體的了解和印象了,然后讓我們繼續(xù)學(xué)習(xí)相關(guān)知識(shí)。

斷言(Predicate)

開發(fā)人員可以匹配HTTP請求中的所有內(nèi)容(例如請求頭或請求參數(shù)),如果請求與斷言相匹配則進(jìn)行路由。
也就是我們進(jìn)行路由跳轉(zhuǎn)的條件。
https://docs.spring.io/spring-cloud-gateway/docs/3.0.2/reference/html/#gateway-request-predicates-factories

在之前我們的配置中使用了如下圖 Path 這樣一種斷言。

image.png

但是實(shí)際上我們還有好多種斷言,我們可以通過官方文檔查詢到,可以看我下面的連接,但是最好自己去官網(wǎng)找到自己響應(yīng)版本的文檔看(鬼知道以后版本會(huì)不會(huì)出新的斷言),并且官方文檔有響應(yīng)的例子以及解釋。
https://docs.spring.io/spring-cloud-gateway/docs/3.0.2/reference/html/#gateway-request-predicates-factories
在我們的gateway服務(wù)啟動(dòng)時(shí)候,控制臺(tái)業(yè)務(wù)打印出來我們斷言的方式。
左圖官方文檔,右圖啟動(dòng)控制臺(tái)信息。

image.pngimage.png

Spring Cloud Gateway將路由匹配作為Spring WebFlux HandlerMapping基礎(chǔ)架構(gòu)的一部分。 Spring Cloud Gateway包括許多內(nèi)置的路由斷言工廠。所有這些謂詞都與HTTP請求的不同屬性匹配。您可以將多個(gè)路由斷言工廠與邏輯和語句結(jié)合使用。

斷言種類:

After

該斷言匹配在指定日期時(shí)間之后發(fā)生的請求。

- After=2017-01-20T17:42:47.789-07:00[America/Denver]

Before

該斷言匹配在指定日期時(shí)間之前發(fā)生的請求。

- Before=2017-01-20T17:42:47.789-07:00[America/Denver]

Between 該斷言匹配在兩個(gè)指定日期時(shí)間之間發(fā)生的請求。

- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]

Cookie

該斷言采用兩個(gè)參數(shù),即cookie名稱和一個(gè)regexp(這是Java正則表達(dá)式)。該斷言匹配具有給定名稱且其值與正則表達(dá)式匹配的cookie。

- Cookie=chocolate, ch.p

Header

該斷言采用兩個(gè)參數(shù),header名稱和一個(gè)regexp(這是Java正則表達(dá)式)。該斷言與具有給定名稱的header匹配,該header的值與正則表達(dá)式匹配。

- Header=X-Request-Id, d+

Host

該斷言采用一個(gè)參數(shù):主機(jī)名模式列表。該模式是帶有的Ant樣式的模式。作為分隔符。

該斷言匹配與模式匹配的Host標(biāo)頭。

- Host=**.somehost.org,**.anotherhost.org

Method

該斷言采用方法參數(shù),該參數(shù)是一個(gè)或多個(gè)參數(shù):要匹配的HTTP方法。

- Method=GET,POST

Path

該斷言采用兩個(gè)參數(shù):Spring PathMatcher模式列表和一個(gè)稱為matchTrailingSlash的可選標(biāo)志(默認(rèn)為true)。

- Path=/red/{segment},/blue/{segment}

Query

該斷言采用兩個(gè)參數(shù):必需的參數(shù)和可選的regexp(這是Java正則表達(dá)式)。如果請求包含匹配配置的查詢參數(shù),則路由匹配。

- Query=green

RemoteAddr

該斷言采用sources列表(最小大小為1),這些源是CIDR標(biāo)記(IPv4或IPv6)字符串,例如192.168.0.1/16(其中192.168.0.1是IP地址,而16是子網(wǎng)掩碼) )。

- RemoteAddr=192.168.1.1/24

ReadBody

文檔中沒寫這個(gè),但是啟動(dòng)的時(shí)候控制臺(tái)顯示了,RoutePredicateFactory的實(shí)現(xiàn)類ReadBodyRoutePredicateFactory寫了一點(diǎn)注釋。斷言可讀取主體并應(yīng)用用戶提供的斷言在主體上運(yùn)行。

主體被緩存在內(nèi)存中,因此后續(xù)對(duì)斷言的調(diào)用無需再次反序列化。

Weight

文檔中沒寫這個(gè),但是啟動(dòng)的時(shí)候控制臺(tái)顯示了,RoutePredicateFactory的實(shí)現(xiàn)類WeightRoutePredicateFactory也沒寫啥注釋

過濾器(Filter)

GatewayFilter允許以某種方式修改傳入的HTTP請求或傳出的HTTP響應(yīng)。路由過濾器的作用域是特定的路由。 Spring Cloud Gateway包括許多內(nèi)置的GatewayFilter工廠。
GlobalFilter接口具有與GatewayFilter相同的簽名。這些是特殊過濾器,有條件地應(yīng)用于所有路由。
https://docs.spring.io/spring-cloud-gateway/docs/3.0.2/reference/html/#gatewayfilter-factories

在我看的這個(gè)文檔中,有兩種Filter(GatewayFilter和GlobalFilter)

image.png

GatewayFilter官網(wǎng)給出了這么31個(gè)過濾器工廠(工廠就是產(chǎn)生對(duì)象的,也就是相當(dāng)于31個(gè)過濾器),
GlobalFilter官網(wǎng)給出了這么10個(gè)過濾器工廠(工廠就是產(chǎn)生對(duì)象的,也就是相當(dāng)于31個(gè)過濾器):

image.pngimage.pngimage.png

怎么用呢?
就像是斷言(Predicate)一樣,在spring.cloud.gateway.routes下面添加就好了,和id、uri同級(jí)。
例如:

image.png

自定義過濾器

我們自定義過濾器需要實(shí)現(xiàn) GlobalFilter,Ordered 這兩個(gè)接口。
implements GlobalFilter,Ordered
能干嘛?

  • 全局日志記錄
  • 統(tǒng)一網(wǎng)關(guān)鑒權(quán)
  • ……

我們的自定義過濾器代碼
MyLogGatewayFilter.java org.example.springcloud.gateway.filter.MyLogGatewayFilter

package org.example.springcloud.gateway.filter;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class MyLogGatewayFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 不用log.info了,為了省點(diǎn)事,別較真
        System.out.println("進(jìn)入到了我們的自定義日志過濾器");
        // request、response啥的都能拿到,要干啥自己看著辦吧,獲取在下兩行,
        // 你是要判斷參數(shù)啊還是判斷token啊還是咋地那你自己定義去吧。
        // ServerHttpRequest request = exchange.getRequest();
        // ServerHttpResponse response = exchange.getResponse();

        // 返回chain.filter(exchange); 表示此過濾器通過,將請求繼續(xù)傳遞下去
        // 交由下面的過濾器繼續(xù)處理
        // 如果是 return null; 則表示被過濾掉了,沒通過。
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        // 加載過濾器的順序,數(shù)值越小優(yōu)先級(jí)越高。
        // Integer.MIN_VALUE 到 Integer.MAX_VALUE
        return 0;
    }
}

寫好了自定義過濾器就ok了,不需要進(jìn)行什么配置,直接@Component加入容器中就可以了。
比如我們在訪問之前的 http://localhost:9002/test/lb ,控制臺(tái)就會(huì)打印我們在過濾器中打印的內(nèi)容,說明進(jìn)入到了過濾器中。

image.png

過濾器很強(qiáng)大,怎么用看個(gè)人、看需求,根據(jù)自己要實(shí)現(xiàn)的內(nèi)容去定制化實(shí)現(xiàn)吧。

對(duì)應(yīng)代碼文件

http://xiazai.jb51.net/202104/yuanma/springcloudgateway_jb51.rar

到此這篇關(guān)于 SpringCloud Gateway 項(xiàng)目最新版本講解的文章就介紹到這了,想要了解更多相關(guān)SpringCloud Gateway內(nèi)容,請搜索W3Cschool以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持!


0 人點(diǎn)贊