SpringCloud GateWay 萬(wàn)字詳解

背景

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

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

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

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

自己手繪的

官網(wǎng)上的

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

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

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

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

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

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

GateWay

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

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

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

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

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

基本概念

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

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

核心流程

核心概念:

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

Filter過(guò)濾器:

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

核心思想

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

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

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

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

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

框架搭建

通過(guò)上述講解已經(jīng)了解了基礎(chǔ)概念,我們來(lái)動(dòng)手搭建一個(gè)GateWay項(xiàng)目,來(lái)看看它到底是如何運(yùn)行的
新建項(xiàng)目:cloud-alibaba-gateway-9006

版本對(duì)應(yīng):

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

在這里我們要注意的是引入GateWay一定要?jiǎng)h除spring-boot-starter-web依賴(lài),否則會(huì)有沖突無(wú)法啟動(dòng)

父類(lèi)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>

子類(lèi)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 #開(kāi)啟注冊(cè)中心路由功能
      routes:  # 路由
        - id: nacos-provider #路由ID,沒(méi)有固定要求,但是要保證唯一,建議配合服務(wù)名
          uri: http://localhost:9001/nacos-provider # 匹配提供服務(wù)的路由地址 lb://表示開(kāi)啟負(fù)載均衡
          predicates: # 斷言
            - Path=/mxn/** # 斷言,路徑相匹配進(jìn)行路由

我們?cè)谥暗?code>cloud-alibaba-nacos-9001項(xiàng)目中添加下面測(cè)試代碼

@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;
    }

 }   

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

首先我們?cè)贜acos中看到我們服務(wù)是注冊(cè)到Nacos中了

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

在上述方法中我們是通過(guò)YML去完成的配置,GateWay還提供了另外一種配置方式,就是通過(guò)代碼的方式進(jìn)行配置,@Bean注入一個(gè) 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 {

      /*
    配置了一個(gè)id為path_mxn的路由規(guī)則
    當(dāng)訪問(wèn)地址http://localhost:9999/mxn/**
    就會(huì)轉(zhuǎn)發(fā)到http://localhost:9001/nacos-provider/mxn/任何地址
     */
    @Bean
    public RouteLocator gateWayConfigInfo(RouteLocatorBuilder routeLocatorBuilder){
        // 構(gòu)建多個(gè)路由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ù),訪問(wèn)地址http://localhost:9006/mxn/hello就可以轉(zhuǎn)發(fā)到9001中具體的接口中

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

GateWay負(fù)載均衡

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

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

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

Predicate 斷言

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

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

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

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

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

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

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

如果在時(shí)間段之前訪問(wèn)則404

Before

匹配ZonedDateTime類(lèi)型的時(shí)間,表示匹配在指定日期時(shí)間之前的請(qǐng)求,之后的請(qǐng)求則拒絕404錯(cuò)誤

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

Between

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

predicates: # 斷言
  - Path=/mxn/** # 斷言,路徑相匹配進(jìn)行路由
# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在這個(gè)時(shí)間之后的請(qǐng)求夠可以進(jìn)行通過(guò),之前的則不能進(jìn)行訪問(wè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

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

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

predicates: # 斷言
  - Path=/mxn/** # 斷言,路徑相匹配進(jìn)行路由
# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在這個(gè)時(shí)間之后的請(qǐng)求夠可以進(jìn)行通過(guò),之前的則不能進(jìn)行訪問(wè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á)式)表示任意字母

小寫(xiě)字母匹配成功:

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

Header

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

predicates: # 斷言
  - Path=/mxn/** # 斷言,路徑相匹配進(jìn)行路由
# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在這個(gè)時(shí)間之后的請(qǐng)求夠可以進(jìn)行通過(guò),之前的則不能進(jìn)行訪問(wè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ù)字

請(qǐng)求頭攜帶數(shù)字?jǐn)嘌哉?qǐng)求成功,

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

Host

匹配當(dāng)前請(qǐng)求是否來(lái)自于設(shè)置的主機(jī)。

predicates: # 斷言
  - Path=/mxn/** # 斷言,路徑相匹配進(jìn)行路由
# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在這個(gè)時(shí)間之后的請(qǐng)求夠可以進(jìn)行通過(guò),之前的則不能進(jìn)行訪問(wè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)前的主機(jī)地址發(fā)出的請(qǐng)求

滿(mǎn)足Host斷言,請(qǐng)求成功

不滿(mǎn)足Host斷言失敗

**Method **

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

predicates: # 斷言
  - Path=/mxn/** # 斷言,路徑相匹配進(jìn)行路由
# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在這個(gè)時(shí)間之后的請(qǐng)求夠可以進(jìn)行通過(guò),之前的則不能進(jìn)行訪問(wè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)前的主機(jī)地址發(fā)出的請(qǐng)求
  - Method=POST,GET

GET斷言成功

PUT斷言請(qǐng)求失敗

Query

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

predicates: # 斷言
  - Path=/mxn/** # 斷言,路徑相匹配進(jìn)行路由
# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在這個(gè)時(shí)間之后的請(qǐng)求夠可以進(jìn)行通過(guò),之前的則不能進(jìn)行訪問(wè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)前的主機(jī)地址發(fā)出的請(qǐng)求
#  - Method=POST,GET
  - Query=id,.+ # 匹配任意請(qǐng)求參數(shù),這里如果需要匹配多個(gè)參數(shù),可以寫(xiě)多個(gè)- Query=

斷言匹配 請(qǐng)求成功

RemoteAddr

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

predicates: # 斷言
  - Path=/mxn/** # 斷言,路徑相匹配進(jìn)行路由
# - After=2022-06-11T16:30:40.785+08:00[Asia/Shanghai] #在這個(gè)時(shí)間之后的請(qǐng)求夠可以進(jìn)行通過(guò),之前的則不能進(jìn)行訪問(wè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)前的主機(jī)地址發(fā)出的請(qǐng)求
#  - Method=POST,GET
#  - Query=id,.+ # 匹配任意請(qǐng)求參數(shù),這里如果需要匹配多個(gè)參數(shù),可以寫(xiě)多個(gè)Query
  - RemoteAddr=192.168.1.1/24

RemoteAddr

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

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

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

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

GateWay Filter

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

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

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

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

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

StripPrefix

StripPrefix 在我們當(dāng)前請(qǐng)求中,通過(guò)規(guī)則值去掉某一部分地址,比如我們有一臺(tái)服務(wù)中加入了一個(gè)前端nacos-provider想要通過(guò)這個(gè)去訪問(wèn),我們?cè)陧?xiàng)目cloud-alibaba-nacos-9001中加入 context-path

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

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

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

我們重新啟動(dòng)9006項(xiàng)目,再去訪問(wèn)

自定義Filter

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

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

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

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

當(dāng)我們?cè)L問(wèn)http://localhost:9006/mxn/nacos-provider/mxn/hello請(qǐng)求,沒(méi)有攜帶ID參數(shù),請(qǐng)求失敗

當(dāng)我們?cè)L問(wèn)http://localhost:9006/mxn/nacos-provider/mxn/hello?id=1請(qǐng)求,請(qǐng)求成功

總結(jié)

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


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


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