SpringCloud GateWay 萬字詳解

背景

在微服務(wù)架構(gòu)中,通常一個系統(tǒng)會被拆分為多個微服務(wù),面對這么多微服務(wù)客戶端應(yīng)該如何去調(diào)用呢?如果沒有其他更優(yōu)方法,我們只能記錄每個微服務(wù)對應(yīng)的地址,分別去調(diào)用,但是這樣會有很多的問題和潛在因素。

  1. 客戶端多次請求不同的微服務(wù),會增加客戶端代碼和配置的復(fù)雜性,維護(hù)成本比價高。
  2. 認(rèn)證復(fù)雜,每個微服務(wù)可能存在不同的認(rèn)證方式,客戶端去調(diào)用,要去適配不同的認(rèn)證,
  3. 存在跨域的請求,調(diào)用鏈有一定的相對復(fù)雜性(防火墻 / 瀏覽器不友好的協(xié)議)。
  4. 難以重構(gòu),隨著項目的迭代,可能需要重新劃分微服務(wù)

為了解決上面的問題,微服務(wù)引入了 網(wǎng)關(guān) 的概念,網(wǎng)關(guān)為微服務(wù)架構(gòu)的系統(tǒng)提供簡單、有效且統(tǒng)一的API路由管理,作為系統(tǒng)的統(tǒng)一入口,提供內(nèi)部服務(wù)的路由中轉(zhuǎn),給客戶端提供統(tǒng)一的服務(wù),可以實現(xiàn)一些和業(yè)務(wù)沒有耦合的公用邏輯,主要功能包含認(rèn)證、鑒權(quán)、路由轉(zhuǎn)發(fā)、安全策略、防刷、流量控制、監(jiān)控日志等。

網(wǎng)關(guān)在微服務(wù)中的位置:

自己手繪的

官網(wǎng)上的

網(wǎng)關(guān)對比

  • Zuul 1.0 : Netflix開源的網(wǎng)關(guān),使用Java開發(fā),基于Servlet架構(gòu)構(gòu)建,便于二次開發(fā)。因為基于Servlet內(nèi)部延遲嚴(yán)重,并發(fā)場景不友好,一個線程只能處理一次連接請求。

  • Zuul 2.0 : 采用Netty實現(xiàn)異步非阻塞編程模型,一個CPU一個線程,能夠處理所有的請求和響應(yīng),請求響應(yīng)的生命周期通過事件和回調(diào)進(jìn)行處理,減少線程數(shù)量,開銷較小

  • GateWay : 是Spring Cloud的一個全新的API網(wǎng)關(guān)項目,替換Zuul開發(fā)的網(wǎng)關(guān)服務(wù),基于Spring5.0 + SpringBoot2.0 + WebFlux(基于?性能的Reactor模式響應(yīng)式通信框架Netty,異步?阻塞模型)等技術(shù)開發(fā),性能高于Zuul

  • Nginx+lua : 性能要比上面的強很多,使用Nginx的反向代碼和負(fù)載均衡實現(xiàn)對API服務(wù)器的負(fù)載均衡以及高可用,lua作為一款腳本語言,可以編寫一些簡單的邏輯,但是無法嵌入到微服務(wù)架構(gòu)中

  • Kong : 基于OpenResty(Nginx + Lua模塊)編寫的高可用、易擴展的,性能高效且穩(wěn)定,支持多個可用插件(限流、鑒權(quán))等,開箱即可用,只支持HTTP協(xié)議,且二次開發(fā)擴展難,缺乏更易用的管理和配置方式

GateWay

官方文檔:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-starter

Spring Cloud Gateway 是Spring Cloud的一個全新的API網(wǎng)關(guān)項目,目的是為了替換掉Zuul1,它基于Spring5.0 + SpringBoot2.0 + WebFlux(基于?性能的Reactor模式響應(yīng)式通信框架Netty,異步?阻塞模型)等技術(shù)開發(fā),性能?于Zuul,官?測試,Spring Cloud GateWay是Zuul的1.6倍 ,旨在為微服務(wù)架構(gòu)提供?種簡單有效的統(tǒng)?的API路由管理?式。

  1. 可以與Spring Cloud Discovery Client(如Eureka)、Ribbon、Hystrix等組件配合使用,實現(xiàn)路由轉(zhuǎn)發(fā)、負(fù)載均衡、熔斷、鑒權(quán)、路徑重寫、?志監(jiān)控等

  2. Gateway還內(nèi)置了限流過濾器,實現(xiàn)了限流的功能。

  3. 設(shè)計優(yōu)雅,容易拓展

基本概念

路由(Route)是GateWay中最基本的組件之一,表示一個具體的路由信息載體,主要由下面幾個部分組成:

  1. id:路由唯一標(biāo)識,區(qū)別于其他的route
  2. url: 路由指向的目的地URL,客戶端請求最終被轉(zhuǎn)發(fā)到的微服務(wù)
  3. order: 用于多個Route之間的排序,數(shù)值越小越靠前,匹配優(yōu)先級越高
  4. predicate:斷言的作用是進(jìn)行條件判斷,只有斷言為true,才執(zhí)行路由
  5. filter: 過濾器用于修改請求和響應(yīng)信息

核心流程

核心概念:

  1. Gateway ClientSpring Cloud Gateway發(fā)送請求
  2. 請求首先會被 HttpWebHandlerAdapter進(jìn)行提取組裝成網(wǎng)關(guān)上下文
  3. 然后網(wǎng)關(guān)的上下文會傳遞到 DispatcherHandler,它負(fù)責(zé)將請求分發(fā)給 RoutePredicateHandlerMapping
  4. RoutePredicateHandlerMapping負(fù)責(zé)路由查找,并根據(jù)路由斷言判斷路由是否可用
  5. 如果過斷言成功,由FilteringWebHandler創(chuàng)建過濾器鏈并調(diào)用
  6. 通過特定于請求的 Fliter鏈運行請求,Filter被虛線分隔的原因是Filter可以在發(fā)送代理請求之前(pre)和之后(post)運行邏輯
  7. 執(zhí)行所有pre過濾器邏輯。然后進(jìn)行代理請求。發(fā)出代理請求后,將運行“post”過濾器邏輯。
  8. 處理完畢之后將 Response返回到 Gateway客戶端

Filter過濾器:

  • Filter在pre類型的過濾器可以做參數(shù)效驗、權(quán)限效驗、流量監(jiān)控、日志輸出、協(xié)議轉(zhuǎn)換等。
  • Filter在post類型的過濾器可以做響應(yīng)內(nèi)容、響應(yīng)頭的修改、日志輸出、流量監(jiān)控等

核心思想

當(dāng)用戶發(fā)出請求達(dá)到 GateWay之后,會通過一些匹配條件,定位到真正的服務(wù)節(jié)點,并且在這個轉(zhuǎn)發(fā)過程前后,進(jìn)行一些細(xì)粒度的控制,其中 Predicate(斷言) 是我們的匹配條件,Filter 是一個攔截器,有了這兩點,再加上URL,就可以實現(xiàn)一個具體的路由,核心思想:路由轉(zhuǎn)發(fā)+執(zhí)行過濾器鏈

這個過程就好比考試,我們考試首先要找到對應(yīng)的考場,我們需要知道考場的地址和名稱(id和url),然后我們進(jìn)入考場之前會有考官查看我們的準(zhǔn)考證是否匹配(斷言),如果匹配才會進(jìn)入考場,我們進(jìn)入考場之后,(路由之前)會進(jìn)行身份的登記和考試的科目,填寫考試信息,當(dāng)我們考試完成之后(路由之后)會進(jìn)行簽字交卷,走出考場,這個就類似我們的過濾器

Route(路由) :構(gòu)建網(wǎng)關(guān)的基礎(chǔ)模塊,由ID、目標(biāo)URL、過濾器等組成

Predicate(斷言) :開發(fā)人員可以匹配HTTP請求中的內(nèi)容(請求頭和請求參數(shù)),如果請求斷言匹配賊進(jìn)行路由

Filter(過濾) :GateWayFilter的實例,使用過濾器,可以在請求被路由之前或者之后對請求進(jìn)行修改

框架搭建

通過上述講解已經(jīng)了解了基礎(chǔ)概念,我們來動手搭建一個GateWay項目,來看看它到底是如何運行的
新建項目:cloud-alibaba-gateway-9006

版本對應(yīng):

GateWay屬于SprinigCloud且有web依賴,在我們導(dǎo)入對應(yīng)依賴時,要注意版本關(guān)系,我們這里使用的版本是 2.2.x的版本,所以配合使用的Hoxton.SR5版本

在這里我們要注意的是引入GateWay一定要刪除spring-boot-starter-web依賴,否則會有沖突無法啟動

父類pom引用:

<spring-cloud-gateway-varsion>Hoxton.SR5</spring-cloud-gateway-varsion>

 <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-dependencies</artifactId>
    <version>${spring-cloud-gateway-varsion}</version>
    <type>pom</type>
    <scope>import</scope>
</dependency>

子類POM引用:

<dependencies>
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
        <version>2.2.5.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
    </dependency>
</dependencies>

yml配置

server:
  port: 9006
spring:
  application:
    name: cloud-gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          enabled: false #開啟注冊中心路由功能
      routes:  # 路由
        - id: nacos-provider #路由ID,沒有固定要求,但是要保證唯一,建議配合服務(wù)名
          uri: http://localhost:9001/nacos-provider # 匹配提供服務(wù)的路由地址 lb://表示開啟負(fù)載均衡
          predicates: # 斷言
            - Path=/mxn/** # 斷言,路徑相匹配進(jìn)行路由

我們在之前的cloud-alibaba-nacos-9001項目中添加下面測試代碼

@RestController
@RequestMapping("/mxn")
public class DemoController {

    @Value("${server.port}")
    private String serverPort;

    @GetMapping(value = "/hello")
    public String hello(){
        return "hello world ,my port is :"+serverPort;
    }

 }   

啟動Nacos、cloud-alibaba-nacos-9001、cloud-alibaba-gateway-9006通過gateway網(wǎng)關(guān)去訪問9001的mxn/order看看。

首先我們在Nacos中看到我們服務(wù)是注冊到Nacos中了

然后我們訪問http://localhost:9001/mxn/hello,確保是成功的,在通過http://localhost:9006/mxn/hello去訪問,也是OK,說明我們GateWay搭建成功,我們進(jìn)入下一步

在上述方法中我們是通過YML去完成的配置,GateWay還提供了另外一種配置方式,就是通過代碼的方式進(jìn)行配置,@Bean注入一個 RouteLocator

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;

@Configuration
public class GateWayConfig {

      /*
    配置了一個id為path_mxn的路由規(guī)則
    當(dāng)訪問地址http://localhost:9999/mxn/**
    就會轉(zhuǎn)發(fā)到http://localhost:9001/nacos-provider/mxn/任何地址
     */
    @Bean
    public RouteLocator gateWayConfigInfo(RouteLocatorBuilder routeLocatorBuilder){
        // 構(gòu)建多個路由routes
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        // 具體路由地址
        routes.route("path_mxn",r -> r.path("/mxn/**").uri("http://localhost:9001/nacos-provider")).build();
        // 返回所有路由規(guī)則
        return routes.build();
    }
}

我們可以將路由注釋掉之后看一下,重啟9006服務(wù),訪問地址http://localhost:9006/mxn/hello就可以轉(zhuǎn)發(fā)到9001中具體的接口中

這里并不推薦,使用代碼的方式來進(jìn)行配置gateWay,大家有個了解就可以,因為代碼的配置維護(hù)的成本比較高,而且對于一些需要修改的項,需要改代碼才可以完成,這樣不利于維護(hù)和拓展,所以還是推薦大家使用yml進(jìn)行配置。

GateWay負(fù)載均衡

在上述的講解中,我們已經(jīng)掌握了 GateWay的一些基本配置和兩種使用方式,下面我們就來講解一下 GateWay如何實現(xiàn)負(fù)載均衡

我們只需要在9006中添加lb://nacos-provider就可以顯示負(fù)載均衡。

當(dāng)我們?nèi)ピL問http://localhost:9006/mxn/hello的時候,就可以看到9001和9002不停的切換

Predicate 斷言

在這一篇中我們來研究一下 斷言 ,我們可以理解為:當(dāng)滿足條件后才會進(jìn)行轉(zhuǎn)發(fā)路由,如果是多個,那么多個條件需要同時滿足

在官方提供的斷言種類有11種(最新的有12種類型):

具體地址:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories

  1. After:匹配在指定日期時間之后發(fā)生的請求。
  2. Before:匹配在指定日期之前發(fā)生的請求。
  3. Between:需要指定兩個日期參數(shù),設(shè)定一個時間區(qū)間,匹配此時間區(qū)間內(nèi)的請求。
  4. Cookie:需要指定兩個參數(shù),分別為name和regexp(正則表達(dá)式),也可以理解Key和Value,匹配具有給定名稱且其值與正則表達(dá)式匹配的Cookie。
  5. Header:需要兩個參數(shù)header和regexp(正則表達(dá)式),也可以理解為Key和Value,匹配請求攜帶信息。
  6. Host:匹配當(dāng)前請求是否來自于設(shè)置的主機。
  7. Method:可以設(shè)置一個或多個參數(shù),匹配HTTP請求,比如GET、POST
  8. Path:匹配指定路徑下的請求,可以是多個用逗號分隔
  9. Query:需要指定一個或者多個參數(shù),一個必須參數(shù)和一個可選的正則表達(dá)式,匹配請求中是否包含第一個參數(shù),如果有兩個參數(shù),則匹配請求中第一個參數(shù)的值是否符合正則表達(dá)式。
  10. RemoteAddr:匹配指定IP或IP段,符合條件轉(zhuǎn)發(fā)。
  11. Weight:需要兩個參數(shù)group和weight(int),實現(xiàn)了路由權(quán)重功能,按照路由權(quán)重選擇同一個分組中的路由

1. After : 表示配置時間之后才進(jìn)行轉(zhuǎn)發(fā)

時間戳獲取代碼,用于時間代碼的獲?。?/p>

    public static void main(String[] args) {
        ZonedDateTime zbj = ZonedDateTime.now();//默認(rèn)時區(qū)
        System.out.println(zbj);
    }
spring:
  application:
    name: cloud-gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          enabled: true #開啟注冊中心路由功能
      routes:  # 路由
        - id: nacos-provider #路由ID,沒有固定要求,但是要保證唯一,建議配合服務(wù)名
          uri: lb://nacos-provider # 匹配提供服務(wù)的路由地址 lb://表示開啟負(fù)載均衡
          predicates: # 斷言
            - Path=/mxn/** # 斷言,路徑相匹配進(jìn)行路由
            - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在這個時間之后的請求夠可以進(jìn)行通過,之前的則不能進(jìn)行訪問

如果在時間段之前訪問則404

Before

匹配ZonedDateTime類型的時間,表示匹配在指定日期時間之前的請求,之后的請求則拒絕404錯誤

predicates: # 斷言
  - Path=/mxn/** # 斷言,路徑相匹配進(jìn)行路由
# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在這個時間之后的請求夠可以進(jìn)行通過,之前的則不能進(jìn)行訪問
  - Before=2022-06-11T15:30:40.785+08:00[Asia/Shanghai]

Between

Between可以匹配ZonedDateTime類型的時間,由兩個ZonedDateTime參數(shù)組成,第一個參數(shù)為開始時間,第二參數(shù)為結(jié)束時間,逗號進(jìn)行分隔,匹配在指定的開始時間與結(jié)束時間之內(nèi)的請求,配置如下:

predicates: # 斷言
  - Path=/mxn/** # 斷言,路徑相匹配進(jìn)行路由
# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在這個時間之后的請求夠可以進(jìn)行通過,之前的則不能進(jìn)行訪問
#  - Before=2022-06-11T15:30:40.785+08:00[Asia/Shanghai]
  - Between=2022-06-11T15:30:40.785+08:00[Asia/Shanghai],2022-06-11T16:30:40.785+08:00[Asia/Shanghai]

Cookie

由兩個參數(shù)組成,分別為name(Key)regexp(正則表達(dá)式)(Value),匹配具有給定名稱且其值與正則表達(dá)式匹配的Cookie。

路由規(guī)則會通過獲取Cookie name值和正則表達(dá)式去匹配,如果匹配上就會執(zhí)行路由,如果匹配不上則不執(zhí)行。

predicates: # 斷言
  - Path=/mxn/** # 斷言,路徑相匹配進(jìn)行路由
# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在這個時間之后的請求夠可以進(jìn)行通過,之前的則不能進(jìn)行訪問
#  - Before=2022-06-11T15:30:40.785+08:00[Asia/Shanghai]
#  - Between=2022-06-11T15:30:40.785+08:00[Asia/Shanghai],2022-06-11T16:30:40.785+08:00[Asia/Shanghai]
   - Cookie=muxiaonong,[a-z]+ # 匹配Cookie的key和value(正則表達(dá)式)表示任意字母

小寫字母匹配成功:

數(shù)字匹配不成功:

Header

由兩個參數(shù)組成,第一個參數(shù)為Header名稱,第二參數(shù)為Header的Value值,指定名稱的其值和正則表達(dá)式相匹配的Header的請求

predicates: # 斷言
  - Path=/mxn/** # 斷言,路徑相匹配進(jìn)行路由
# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在這個時間之后的請求夠可以進(jìn)行通過,之前的則不能進(jìn)行訪問
#  - Before=2022-06-11T15:30:40.785+08:00[Asia/Shanghai]
#  - Between=2022-06-11T15:30:40.785+08:00[Asia/Shanghai],2022-06-11T16:30:40.785+08:00[Asia/Shanghai]
#  - Cookie=muxiaonong,[a-z]+ # 匹配Cookie的key和value(正則表達(dá)式)表示任意字母
  - Header=headerName, \d+ # \d表示數(shù)字

請求頭攜帶數(shù)字?jǐn)嘌哉埱蟪晒Γ?/p>

斷言字母匹配失?。?/p>

Host

匹配當(dāng)前請求是否來自于設(shè)置的主機。

predicates: # 斷言
  - Path=/mxn/** # 斷言,路徑相匹配進(jìn)行路由
# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在這個時間之后的請求夠可以進(jìn)行通過,之前的則不能進(jìn)行訪問
#  - Before=2022-06-11T15:30:40.785+08:00[Asia/Shanghai]
#  - Between=2022-06-11T15:30:40.785+08:00[Asia/Shanghai],2022-06-11T16:30:40.785+08:00[Asia/Shanghai]
#  - Cookie=muxiaonong,[a-z]+ # 匹配Cookie的key和value(正則表達(dá)式)表示任意字母
#  - Header=headerName, \d+ # \d表示數(shù)字
  - Host=**.muxiaonong.com #匹配當(dāng)前的主機地址發(fā)出的請求

滿足Host斷言,請求成功

不滿足Host斷言失敗

**Method **

可以設(shè)置一個或多個參數(shù),匹配HTTP請求,比如POST,PUT,GET,DELETE

predicates: # 斷言
  - Path=/mxn/** # 斷言,路徑相匹配進(jìn)行路由
# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在這個時間之后的請求夠可以進(jìn)行通過,之前的則不能進(jìn)行訪問
#  - Before=2022-06-11T15:30:40.785+08:00[Asia/Shanghai]
#  - Between=2022-06-11T15:30:40.785+08:00[Asia/Shanghai],2022-06-11T16:30:40.785+08:00[Asia/Shanghai]
#  - Cookie=muxiaonong,[a-z]+ # 匹配Cookie的key和value(正則表達(dá)式)表示任意字母
#  - Header=headerName, \d+ # \d表示數(shù)字
#  - Host=**.muxiaonong.com #匹配當(dāng)前的主機地址發(fā)出的請求
  - Method=POST,GET

GET斷言成功

PUT斷言請求失敗

Query

由兩個參數(shù)組成,第一個為參數(shù)名稱(必須),第二個為參數(shù)值(可選-正則表達(dá)式),匹配請求中是否包含第一個參數(shù),如果有兩個參數(shù),則匹配請求中第一個參數(shù)的值是否符合第二個正則表達(dá)式。

predicates: # 斷言
  - Path=/mxn/** # 斷言,路徑相匹配進(jìn)行路由
# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在這個時間之后的請求夠可以進(jìn)行通過,之前的則不能進(jìn)行訪問
#  - Before=2022-06-11T15:30:40.785+08:00[Asia/Shanghai]
#  - Between=2022-06-11T15:30:40.785+08:00[Asia/Shanghai],2022-06-11T16:30:40.785+08:00[Asia/Shanghai]
#  - Cookie=muxiaonong,[a-z]+ # 匹配Cookie的key和value(正則表達(dá)式)表示任意字母
#  - Header=headerName, \d+ # \d表示數(shù)字
#  - Host=**.muxiaonong.com #匹配當(dāng)前的主機地址發(fā)出的請求
#  - Method=POST,GET
  - Query=id,.+ # 匹配任意請求參數(shù),這里如果需要匹配多個參數(shù),可以寫多個- Query=

斷言匹配 請求成功

RemoteAddr

參數(shù)由CIDR 表示法(IPv4 或 IPv6)字符串組成,也就是匹配的ID地址,配置如下:

predicates: # 斷言
  - Path=/mxn/** # 斷言,路徑相匹配進(jìn)行路由
# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在這個時間之后的請求夠可以進(jìn)行通過,之前的則不能進(jìn)行訪問
#  - Before=2022-06-11T15:30:40.785+08:00[Asia/Shanghai]
#  - Between=2022-06-11T15:30:40.785+08:00[Asia/Shanghai],2022-06-11T16:30:40.785+08:00[Asia/Shanghai]
#  - Cookie=muxiaonong,[a-z]+ # 匹配Cookie的key和value(正則表達(dá)式)表示任意字母
#  - Header=headerName, \d+ # \d表示數(shù)字
#  - Host=**.muxiaonong.com #匹配當(dāng)前的主機地址發(fā)出的請求
#  - Method=POST,GET
#  - Query=id,.+ # 匹配任意請求參數(shù),這里如果需要匹配多個參數(shù),可以寫多個Query
  - RemoteAddr=192.168.1.1/24

RemoteAddr

需要兩個參數(shù)group和weight(int)權(quán)重數(shù)值,實現(xiàn)了路由權(quán)重功能,表示將相同的請求根據(jù)權(quán)重跳轉(zhuǎn)到不同的uri地址,要求group的名稱必須一致

routes:  # 路由
  - id: weight_high #路由ID,沒有固定要求,但是要保證唯一,建議配合服務(wù)名
    uri: https://blog.csdn.net/qq_14996421
    predicates: # 斷言
      - Weight=groupName,8
  - id: weight_low #路由ID,沒有固定要求,但是要保證唯一,建議配合服務(wù)名
    uri: https://juejin.cn/user/2700056290405815
    predicates: # 斷言
      - Weight=groupName,2

直接訪問http://localhost:9006/可以看到我們請求的地址成8/2比例交替顯示, 80% 的流量轉(zhuǎn)發(fā)到https://blog.csdn.net/qq_14996421,將約 20% 的流量轉(zhuǎn)發(fā)https://juejin.cn/user/2700056290405815

Predicate就是為了實現(xiàn)一組匹配規(guī)則,讓請求過來找到對應(yīng)的Route進(jìn)行處理。如果有多個斷言則全部命中后進(jìn)行處理

GateWay Filter

路由過濾器允許修改傳入的HTTP請求或者返回的HTTP響應(yīng),路由過濾器的范圍是特定的路由.

Spring Cloud GateWay 內(nèi)置的Filter生命周期有兩種:pre(業(yè)務(wù)邏輯之前)、post(業(yè)務(wù)邏輯之后)

GateWay本身自帶的Filter分為兩種: GateWayFilter(單一)、GlobalFilter(全局)

GateWay Filter提供了豐富的過濾器的使用,單一的有32種,全局的有9種,有興趣的小伙伴可以了解一下。

官方參考網(wǎng)址:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#global-filters

StripPrefix

StripPrefix 在我們當(dāng)前請求中,通過規(guī)則值去掉某一部分地址,比如我們有一臺服務(wù)中加入了一個前端nacos-provider想要通過這個去訪問,我們在項目cloud-alibaba-nacos-9001中加入 context-path

server:
  port: 9001
  servlet:
    context-path: /nacos-provider

現(xiàn)在9001的訪問路徑變?yōu)?code>http://localhost:9001/nacos-provider/mxn/hello,但是如果我們通過網(wǎng)關(guān)去訪問路徑就會變成http://localhost:9006/mxn/nacos-provider/mxn/hello這個時候我們通過這個路徑去訪問是訪問不成功的,想要解決這個方法,這個就用到了我們FIlter中的 StripPrefix

routes:  # 路由
  - id: nacos-provider #路由ID,沒有固定要求,但是要保證唯一,建議配合服務(wù)名
    uri: lb://nacos-provider
    predicates: # 斷言
      - Path=/mxn/** # 匹配對應(yīng)地址
    filters:
      - StripPrefix=1 # 去掉地址中的第一部分

我們重新啟動9006項目,再去訪問

自定義Filter

雖然Gateway給我們提供了豐富的內(nèi)置Filter,但是實際項目中,自定義Filter的場景非常常見,因此單獨介紹下自定義FIlter的使用。

想要實現(xiàn)GateWay自定義過濾器,那么我們需要實現(xiàn)GatewayFilter接口和Ordered接口

@Slf4j
@Component
public class MyFilter implements Ordered, GlobalFilter {
    /**
     * @param exchange 可以拿到對應(yīng)的request和response
     * @param chain 過濾器鏈
     * @return 是否放行
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //獲取第一個參數(shù)
        String id = exchange.getRequest().getQueryParams().getFirst("id");
        //打印當(dāng)前時間
        log.info("MyFilter 當(dāng)前請求時間為:"+new Date());
        //判斷用戶是否存在
        if(StringUtils.isEmpty(id)){
            log.info("用戶名不存在,非法請求!");
            //如果username為空,返回狀態(tài)碼為407,需要代理身份驗證
            exchange.getResponse().setStatusCode(HttpStatus.PROXY_AUTHENTICATION_REQUIRED);
            // 后置過濾器
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    /**
     * 設(shè)定過濾器的優(yōu)先級,值越小則優(yōu)先級越高
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

當(dāng)我們訪問http://localhost:9006/mxn/nacos-provider/mxn/hello請求,沒有攜帶ID參數(shù),請求失敗

當(dāng)我們訪問http://localhost:9006/mxn/nacos-provider/mxn/hello?id=1請求,請求成功

總結(jié)

到這里我們的GateWay就講解完了,對于GateWay的核心點主要有三個Route\Predicate\Filter,我們搞懂了這三點,基本上對于GateWay的知識就掌握的差不多了,GateWay核心的流程就是:路由轉(zhuǎn)發(fā)+執(zhí)行過濾器鏈,如果對文中有疑問的小伙伴,歡迎留言討論。


文章轉(zhuǎn)載自: https://muxiaonong.blog.csdn.net


公眾號:牧小農(nóng),微信掃碼關(guān)注或搜索公眾號名稱