第二代微服務(wù)網(wǎng)關(guān)組件 - Spring Cloud Gateway

[TOC]


初識Spring Cloud Gateway

簡介:

Spring Cloud Gateway是Spring Cloud體系的第二代網(wǎng)關(guān)組件,基于Spring 5.0的新特性WebFlux進(jìn)行開發(fā),底層網(wǎng)絡(luò)通信框架使用的是Netty,所以其吞吐量高、性能強(qiáng)勁,未來將會(huì)取代第一代的網(wǎng)關(guān)組件Zuul。Spring Cloud Gateway可以通過服務(wù)發(fā)現(xiàn)組件自動(dòng)轉(zhuǎn)發(fā)請求,默認(rèn)集成了Ribbon做負(fù)載均衡,以及默認(rèn)使用Hystrix對網(wǎng)關(guān)進(jìn)行保護(hù),當(dāng)然也可以選擇其他的容錯(cuò)組件,例如Sentinel

優(yōu)點(diǎn):

  • 性能強(qiáng)勁:是第一代網(wǎng)關(guān)Zuul的1.6倍
  • 功能強(qiáng)大:內(nèi)置了很多實(shí)用的功能,例如轉(zhuǎn)發(fā)、監(jiān)控、限流等
  • 設(shè)計(jì)優(yōu)雅,容易擴(kuò)展

缺點(diǎn):

  • 其實(shí)現(xiàn)依賴Netty與WebFlux,不是傳統(tǒng)的Servlet編程模型,有一定的學(xué)習(xí)成本
  • 不能在Servlet容器下工作,也不能構(gòu)建成WAR包,即不能將其部署在Tomcat、Jetty等Servlet容器里,只能打成jar包執(zhí)行
  • 不支持Spring Boot 1.x,需2.0及更高的版本

如果對網(wǎng)關(guān)概念或Zuul不了解的話,可以參考另一篇文章:

核心概念:

1、Route(路由):

Spring Cloud Gateway的基礎(chǔ)元素,可簡單理解成一條轉(zhuǎn)發(fā)規(guī)則。包含:ID、目標(biāo)URL、Predicate集合以及Filter集合

這是一段比較典型的Gateway路由配置:

spring:
  cloud:
    gateway:
      routes:
      - id: user-center  # 唯一標(biāo)識,通常使用服務(wù)id
        uri: lb://user-center  # 目標(biāo)URL,lb代表從注冊中心獲取服務(wù),lb是Load Balance的縮寫
        predicates:
        # Predicate集合
        - Path=/zj/cloud/v1/user-center/**  # 匹配轉(zhuǎn)發(fā)路徑
        filters:
        # Filter集合
        - StripPrefix=4  # 從第幾級開始轉(zhuǎn)發(fā)

2、Predicate(謂詞):

java.util.function.Predicate這個(gè)接口,Gateway使用Predicate實(shí)現(xiàn)路由的匹配條件

3、Filter(過濾器):

與我們平時(shí)使用的Servlet編程模型里的過濾器概念類似,同樣可以用于修改請求以及響應(yīng)數(shù)據(jù),可以利用Filter實(shí)現(xiàn)鑒權(quán)、訪問日志記錄,接口耗時(shí)記錄等功能

Spring Cloud Gateway架構(gòu)圖:

image.png

簡單解讀一下這個(gè)圖:

Gateway Client發(fā)送請求給Spring Cloud Gateway,Gateway Handler Mapping會(huì)判斷請求的路徑是否匹配路由的配置,如果匹配則會(huì)進(jìn)入Gateway Web Handler,Web Handler會(huì)讀取路由上所配置的過濾器,然后將該請求交給過濾器去處理,最后轉(zhuǎn)發(fā)到路由配置的微服務(wù)上

  • Gateway Client:泛指外部請求,例如瀏覽器、app、小程序等
  • Proxied Service:指的是被網(wǎng)關(guān)代理的微服務(wù)

相關(guān)源碼:

  • Gateway Handler Mapping:org.springframework.cloud.gateway.handler.RoutePredicateHandlerMapping
  • Gateway Web Handler:org.springframework.cloud.gateway.handler.FilteringWebHandler

由于Webflux大量運(yùn)用函數(shù)式編程思想,所以本文中的示例代碼都會(huì)使用lambda表達(dá)式及函數(shù)式API來簡化。若對此不了解的話,可以參考相關(guān)文章,篇幅有限這里就不進(jìn)行介紹了:


創(chuàng)建Spring Cloud Gateway項(xiàng)目

這里使用IDEA的Spring Initializr進(jìn)行項(xiàng)目的創(chuàng)建,到選擇依賴這一步勾選gateway依賴,如下圖:


image.png

網(wǎng)關(guān)組件一般都配合服務(wù)發(fā)現(xiàn)組件使用,我這里使用Nacos作為服務(wù)發(fā)現(xiàn)組件,具體的依賴如下:

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
    <!-- Nacos Client -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!-- actuator -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <!--整合Spring Cloud-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Greenwich.SR2</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.1.0.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

如果對Nacos不熟悉的話可以參考另一篇關(guān)于Nacos的文章,或者采用Eureka也是一樣的:

然后編寫配置文件內(nèi)容如下:

server:
  port: 8040
spring:
  application:
    name: gateway
  cloud:
    nacos:
      discovery:
        # 指定nacos server的地址
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          # 讓gateway通過服務(wù)發(fā)現(xiàn)組件找到其他的微服務(wù),從而自動(dòng)轉(zhuǎn)發(fā)請求
          enabled: true

# actuator相關(guān)配置
management:
  endpoints:
    web:
      exposure:
        # 暴露所有監(jiān)控端點(diǎn)
        include: '*'
  endpoint:
    health:
      # 總是顯示健康檢測詳情
      show-details: always

完成以上步驟后,我們來啟動(dòng)這個(gè)網(wǎng)關(guān)服務(wù),進(jìn)行一個(gè)簡單的測試,看看是否能將請求正常地轉(zhuǎn)發(fā)到指定的微服務(wù)上。此時(shí)有一個(gè)名為user-center的微服務(wù),該微服務(wù)有一個(gè)按id獲取用戶信息的接口,接口路徑為/users/{id}。若通過網(wǎng)關(guān)服務(wù)來訪問這個(gè)接口,要如何做呢?很簡單,gateway配合服務(wù)發(fā)現(xiàn)組件使用時(shí),會(huì)有一個(gè)默認(rèn)的轉(zhuǎn)發(fā)規(guī)則,如下:

  • ${GATEWAY_URL}/{微服務(wù)名稱}/{接口路徑}

所以按該規(guī)則得出來的具體url為:localhost:8040/user-center/users/{id},訪問結(jié)果如下:

image.png

從測試結(jié)果可以看到,gateway可以根據(jù)url上的微服務(wù)名稱將訪問請求轉(zhuǎn)發(fā)到該微服務(wù)上。

以上這種是Gateway最簡單的使用方式,但通常在實(shí)際開發(fā)中,可能不希望使用默認(rèn)的轉(zhuǎn)發(fā)規(guī)則,因?yàn)檫@種方式不太靈活,例如一些服務(wù)接口是存在版本劃分的,需要根據(jù)不同版本的訪問路徑轉(zhuǎn)發(fā)到不同版本的微服務(wù)上。此時(shí)就需要自定義轉(zhuǎn)發(fā)路由,實(shí)際上在第一小節(jié)的時(shí)候就已經(jīng)給出過配置示例了。修改配置如下:

spring:
  cloud:
    gateway:
      routes:
      - id: user-center  # 唯一標(biāo)識,通常使用服務(wù)id
        uri: lb://user-center  # 目標(biāo)URL,lb代表從注冊中心獲取服務(wù)
        predicates:
        # Predicate集合
        - Path=/zj/cloud/v1/user-center/**  # 匹配轉(zhuǎn)發(fā)路徑
        filters:
        # Filter集合
        - StripPrefix=4  # 從第幾級開始轉(zhuǎn)發(fā),數(shù)字從0開始

自定義路由的注意事項(xiàng):

  • predicates配置項(xiàng)必須有,且必須配置一個(gè)及以上的Predicate,但不一定非要配置Path,可以配置其他的Predicate,例如After、Before等,此時(shí)Path的默認(rèn)值為/**

重啟項(xiàng)目,此時(shí)訪問的url為:localhost:8040/zj/cloud/v1/user-center/users/{id},訪問結(jié)果如下:

image.png


路由配置的兩種形式

Spring Cloud Gateway的路由配置有兩種形式,分別是路由到指定的URL以及路由到指定的微服務(wù),在上一小節(jié)的示例中我們就已經(jīng)使用過路由到微服務(wù)的這種配置形式了。在這兩種形式中,均支持訪問路徑的通配及精確匹配,在之前的示例中我們只使用了通配。所以本小節(jié)將給出具體的配置示例,以此直觀的了解這兩種形式及不同匹配方式在配置上的區(qū)別。

1、路由到指定的URL

通配,使用通配符/**進(jìn)行匹配,示例:

spring:
  cloud:
    gateway:
      routes:
        - id: test_route  # 路由的唯一標(biāo)識
          uri: http://www.xxx.com
          predicates:
            # 使用通配符匹配
            - Path=/**
  • 該配置使訪問 GATEWAY_URL/** 時(shí)會(huì)轉(zhuǎn)發(fā)到 http://www.xxx.com/**

精確匹配,配置具體的接口路徑即可,示例:

spring:
  cloud:
    gateway:
      routes:
        - id: test_route  # 路由的唯一標(biāo)識
          uri: http://www.xxx.com/user/order/detail
          predicates:
            # 指定具體的路徑進(jìn)行匹配
            - Path=/user/order/detail
  • 該配置使訪問 GATEWAY_URL/user/order/detail 時(shí)會(huì)轉(zhuǎn)發(fā)到 http://www.xxx.com/user/order/detail

2、路由到指定的微服務(wù)

通配,示例:

spring:
  cloud:
    gateway:
      routes:
        - id: user-center  # 路由的唯一標(biāo)識,這種形式下通常是微服務(wù)名稱
          uri: lb://user-center  # lb代表從注冊中心獲取服務(wù)
          predicates:
            # 使用通配符匹配
            - Path=/**
  • 該配置使訪問 GATEWAY_URL/** 時(shí)會(huì)轉(zhuǎn)發(fā)到 user-center微服務(wù)的/**

精確匹配,示例:

spring:
  cloud:
    gateway:
      routes:
        - id: user-center  # 路由的唯一標(biāo)識,這種形式下通常是微服務(wù)名稱
          uri: lb://user-center/users/info  # lb代表從注冊中心獲取服務(wù)
          predicates:
            # 指定具體的路徑進(jìn)行匹配
            - Path=/users/info
  • 該配置使訪問 GATEWAY_URL/users/info 時(shí)會(huì)轉(zhuǎn)發(fā)到 user-center微服務(wù)的/users/info

路由謂詞工廠

前面提到過謂詞是路由的判斷條件,而路由謂詞工廠就是作用到指定路由上的一堆謂詞判斷條件。在之前的示例里,我們就已經(jīng)使用過路由謂詞工廠了,就是自定義轉(zhuǎn)發(fā)路徑時(shí)所配置的Path。

內(nèi)置的路由謂詞工廠

Spring Cloud Gateway內(nèi)置了眾多路由謂詞工廠,這些路由謂詞工廠為路由匹配的判斷提供了有力的支持,而我們之前所使用的Path就是內(nèi)置的路由謂詞工廠之一,用于判斷當(dāng)前訪問的接口路徑是否與該路由所配置的路徑相匹配,若匹配則進(jìn)行轉(zhuǎn)發(fā)。由于Gateway內(nèi)置的路由謂詞工廠比較多,篇幅有限就不在本文中介紹了,可以參考另一篇文章:

自定義路由謂詞工廠

現(xiàn)在我們已經(jīng)知道Spring Cloud Gateway內(nèi)置了一系列的路由謂詞工廠,但如果這些內(nèi)置的路由謂詞工廠不能滿足業(yè)務(wù)需求的話,我們可以自定義路由謂詞工廠來實(shí)現(xiàn)特定的需求。例如有某個(gè)服務(wù)限制用戶只允許在09:00 - 17:00這個(gè)時(shí)間段內(nèi)才可以訪問,內(nèi)置的路由謂詞工廠是無法滿足這個(gè)需求的,所以此時(shí)我們就需要自定義能夠?qū)崿F(xiàn)該需求的路由謂詞工廠。

首先定義一個(gè)配置類,用于承載時(shí)間段的配置參數(shù):

@Data
public class TimeBetweenConfig {
    /**
     * 開始時(shí)間
     */
    private LocalTime start;

    /**
     * 結(jié)束時(shí)間
     */
    private LocalTime end;
}

然后定義一個(gè)路由謂詞工廠,具體代碼如下:

package com.zj.node.gateway.predicate;

import com.zj.node.gateway.config.TimeBetweenConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import java.time.LocalTime;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

/**
 * 路由謂詞工廠必須以RoutePredicateFactory結(jié)尾,
 * 這是Spring Cloud Gateway的約定
 *
 * @author 01
 * @date 2019-08-14
 **/
@Slf4j
@Component
public class TimeBetweenRoutePredicateFactory extends AbstractRoutePredicateFactory<TimeBetweenConfig> {

    public TimeBetweenRoutePredicateFactory() {
        super(TimeBetweenConfig.class);
    }

    /**
     * 實(shí)現(xiàn)謂詞判斷的方法
     */
    @Override
    public Predicate<ServerWebExchange> apply(TimeBetweenConfig config) {
        return exchange -> {
            LocalTime start = config.getStart();
            LocalTime end = config.getEnd();
            
            // 判斷當(dāng)前時(shí)間是否為允許訪問的時(shí)間段內(nèi)
            LocalTime now = LocalTime.now();
            return now.isAfter(start) && now.isBefore(end);
        };
    }

    /**
     * 控制配置類(TimeBetweenConfig)屬性和配置文件中配置項(xiàng)(TimeBetween)的映射關(guān)系
     */
    @Override
    public List<String> shortcutFieldOrder() {
        /*
         * 例如我們的配置項(xiàng)是:TimeBetween=上午9:00, 下午5:00
         * 那么按照順序,start對應(yīng)的是上午9:00;end對應(yīng)的是下午5:00
         **/
        return Arrays.asList("start", "end");
    }
}

最后需要在配置文件中啟用該路由謂詞工廠,并且需要禁止gateway通過服務(wù)發(fā)現(xiàn)組件轉(zhuǎn)發(fā)請求到其他的微服務(wù),修改Gateway相關(guān)配置如下:

spring:
  cloud:
    gateway:
      discovery:
        locator:
          # 禁止gateway通過服務(wù)發(fā)現(xiàn)組件轉(zhuǎn)發(fā)請求到其他的微服務(wù)
          enabled: false
      routes:
        - id: user-center
          # 目標(biāo)URL,lb代表從注冊中心獲取服務(wù)
          uri: lb://user-center
          predicates:
            # 注意名稱必須為路由謂詞工廠類名的前綴,參數(shù)為允許訪問的時(shí)間段
            - TimeBetween=上午9:00,下午5:00

可以看到這里主要是配置了我們自定義的路由謂詞工廠類名的前綴以及允許訪問的時(shí)間段,這個(gè)時(shí)間格式不是隨便配置的,而是Spring Cloud Gateway的默認(rèn)時(shí)間格式,相關(guān)源碼如下:

  • org.springframework.format.support.DefaultFormattingConversionService#addDefaultFormatters

時(shí)間格式是可以注冊的,關(guān)于時(shí)間格式注冊的相關(guān)源碼如下:

  • org.springframework.format.datetime.standard.DateTimeFormatterRegistrar#registerFormatters

另外,這里之所以要禁止gateway通過服務(wù)發(fā)現(xiàn)組件轉(zhuǎn)發(fā)請求到其他的微服務(wù),是因?yàn)殚_啟該配置項(xiàng)的話會(huì)導(dǎo)致我們自定義的路由謂詞工廠不生效。不生效也是有原因的,開啟該配置項(xiàng)會(huì)令Gateway優(yōu)先將請求按照該配置項(xiàng)進(jìn)行轉(zhuǎn)發(fā),那么我們自定義的路由就不會(huì)生效。

到此為止我們就實(shí)現(xiàn)了一個(gè)自定義路由謂詞工廠,若此時(shí)不在允許的訪問時(shí)間段內(nèi),訪問就會(huì)報(bào)404,如下:


image.png

過濾器工廠

前面提到了過濾器可以為請求和響應(yīng)添加一些業(yè)務(wù)邏輯或者修改請求和響應(yīng)對象等,適當(dāng)?shù)厥褂眠^濾器可以讓我們的工作事半功倍,而本小節(jié)將要介紹的過濾器工廠就是用來創(chuàng)建過濾器的。在此之前我們已經(jīng)學(xué)習(xí)過路由謂詞工廠了,而過濾器工廠與路由謂詞工廠在使用上是類似的,只不過實(shí)現(xiàn)的功能不一樣。

內(nèi)置的過濾器工廠

同樣的Spring Cloud Gateway內(nèi)置了非常多的過濾器工廠,有二十多個(gè)。通過這些內(nèi)置的過濾器工廠就已經(jīng)可以靈活且方便地處理請求和響應(yīng)數(shù)據(jù),由于Gateway內(nèi)置的過濾器工廠實(shí)在太多,而篇幅有限就不在本文中介紹了,可以參考另一篇文章:

自定義過濾器工廠

若Spring Cloud Gateway內(nèi)置的過濾器工廠無法滿足我們的業(yè)務(wù)需求,那么此時(shí)就需要自定義自己的過濾器工廠以實(shí)現(xiàn)特定功能。所謂過濾器工廠實(shí)際上就是用于創(chuàng)建過濾器實(shí)例的,而創(chuàng)建的過濾器實(shí)例都實(shí)現(xiàn)于GatewayFilter接口。

過濾器的生命周期:

  • Gateway以轉(zhuǎn)發(fā)請求為邊界,所以其生命周期只包含pre和post:
    • pre:Gateway轉(zhuǎn)發(fā)請求之前
    • post:Gateway轉(zhuǎn)發(fā)請求之后

自定義過濾器工廠的方式:

  1. 繼承AbstractGatewayFilterFactory,參考源碼:org.springframework.cloud.gateway.filter.factory.RequestSizeGatewayFilterFactory。使用該方式實(shí)現(xiàn)的過濾器工廠的配置形式如下:
spring:
  cloud:
    gateway:
      routes:
        filters:
        # 過濾器工廠的名稱
        - name: RequestSize
          # 該過濾器工廠的參數(shù)
          args:
            maxSize: 500000
  1. 繼承AbstractNameValueGatewayFilterFactory,參考源碼:org.springframework.cloud.gateway.filter.factory.AddRequestHeaderGatewayFilterFactory。使用該方式實(shí)現(xiàn)的過濾器工廠的配置形式如下:
spring:
  cloud:
    gateway:
      routes:
        filters:
        # 過濾器工廠的名稱及參數(shù)以name-value的形式配置
        - AddRequestHeader=S-Header, Bar

注:AbstractNameValueGatewayFilterFactory繼承了AbstractGatewayFilterFactory,所以實(shí)際上第二種方式是第一種方式的簡化

核心API:

  • exchange.getRequest().mutate().xxx:修改request
  • exchange.mutate().xxx:修改exchange
  • chain.filter(exchange):傳遞給下一個(gè)過濾器處理
  • exchange.getResponse():獲取響應(yīng)對象

注:這里的exchange實(shí)際類型為ServerWebExchange,chain實(shí)際類型為GatewayFilter

最后我們來實(shí)際動(dòng)手編寫一個(gè)自定義過濾器工廠,需求是記錄訪問日志,這里為了簡單起見采用第二種方式實(shí)現(xiàn),具體代碼如下:

package com.zj.node.gateway.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractNameValueGatewayFilterFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

/**
 * 過濾器工廠必須以GatewayFilterFactory結(jié)尾,
 * 這是Spring Cloud Gateway的約定
 *
 * @author 01
 * @date 2019-08-15
 **/
@Slf4j
@Component
public class PreLogGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
    
    @Override
    public GatewayFilter apply(NameValueConfig config) {
        // 使用lambda表達(dá)式來創(chuàng)建GatewayFilter的實(shí)例,實(shí)際就是匿名內(nèi)部類的簡寫
        return (exchange, chain) -> {
            // 通過config獲取配置的參數(shù)
            log.info("配置參數(shù):{}, {}", config.getName(), config.getValue());

            // 修改request,可以添加一些header什么的
            ServerHttpRequest modifiedRequest = exchange.getRequest()
                    .mutate()
                    .header("X-GatewayHeader","A","B")
                    .build();

            // 打印訪問的接口地址
            String path = modifiedRequest.getURI().getPath();
            log.info("訪問的接口為:{}", path);

            // 修改exchange
            ServerWebExchange modifiedExchange = exchange.mutate()
                    .request(modifiedRequest).build();

            // 傳遞給下一個(gè)過濾器處理
            return chain.filter(modifiedExchange);
        };
    }
}

最后需要添加相關(guān)配置以啟用這個(gè)過濾器工廠,如下:

spring:
  cloud:
    gateway:
      routes:
        - id: user-center
          uri: lb://user-center
          predicates:
            - TimeBetween=上午9:00,下午5:00
          filters:
            # 名稱必須為過濾器工廠類名的前綴,并且參數(shù)只能有兩個(gè),因?yàn)镹ameValueConfig里只定義了兩個(gè)屬性
            - PreLog=testName,testValue

啟動(dòng)項(xiàng)目,訪問user-center的接口,此時(shí)控制臺輸出的日志如下:


image.png

全局過濾器

現(xiàn)在我們已經(jīng)知道前面所介紹的過濾器工廠實(shí)際用于創(chuàng)建GatewayFilter實(shí)例,并且這些GatewayFilter實(shí)例僅作用于指定的路由上,那么有沒有可以作用于全部路由上的過濾器呢?答案是有的,這就是本小節(jié)將要介紹的全局過濾器。Spring Cloud Gateway默認(rèn)就內(nèi)置了許多全局過濾器,本文僅介紹如何自定義全局過濾器,關(guān)于Gateway內(nèi)置的過濾器可以參考另一篇文章:

自定義全局過濾需要實(shí)現(xiàn)GlobalFilter 接口,該接口和 GatewayFilter 有一樣的方法定義,只不過 GlobalFilter 的實(shí)例會(huì)作用于所有的路由。

Tips:

官方聲明:GlobalFilter的接口定義以及用法在未來的版本可能會(huì)發(fā)生變化。

個(gè)人判斷:GlobalFilter可用于生產(chǎn);如果有自定義GlobalFilter的需求,理論上也可放心使用。因?yàn)槲磥砑词菇涌诙x以及使用方式發(fā)生變化,理應(yīng)也是平滑過渡的(比如Zuul的Fallback,原先叫ZuulFallbackProvider,后來改叫FallbackProvider,中間就有段時(shí)間新舊使用方式都支持,后面才逐步廢棄老的使用方式)。

接下來我們自定義一個(gè)全局過濾器,需求是打印訪問的接口路徑以及打印該接口的訪問耗時(shí)。具體代碼如下:

package com.zj.node.gateway.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * 自定義全局過濾器
 *
 * @author 01
 * @date 2019-08-17
 **/
@Slf4j
public class MyGlobalFilter implements GlobalFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String path = exchange.getRequest().getURI().getPath();
        log.info("[MyGlobalFilter] 訪問的接口:{}", path);

        long start = System.currentTimeMillis();
        return chain.filter(exchange)
                // then的內(nèi)容會(huì)在過濾器返回的時(shí)候執(zhí)行,即最后執(zhí)行
                .then(Mono.fromRunnable(() ->
                        log.info("[ {} ] 接口的訪問耗時(shí):{} /ms", 
                        path, System.currentTimeMillis() - start))
                );
    }
}

最后需要使該全局過濾器生效,方法有很多種,可以直接在該類上加@Component注解,也可以通過代碼配置(@Bean),還有其他的一些方式。這里個(gè)人比較傾向于使用一個(gè)專門的配置類去實(shí)例化這些全局過濾器并交給Spring容器管理。代碼如下:

package com.zj.node.gateway.config;

import com.zj.node.gateway.filter.MyGlobalFilter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;

@Slf4j
@Configuration
public class FilterConfig {

    @Bean
    // 該注解用于指定過濾器的執(zhí)行順序,數(shù)字越小越優(yōu)先執(zhí)行
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter myGlobalFilter(){
        log.info("create myGlobalFilter...");
        return new MyGlobalFilter();
    }
}

啟動(dòng)項(xiàng)目,看看我們自定義的全局過濾器是否已生效,訪問Gateway控制臺輸出如下:


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

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

  • 小外甥11歲生日,今年剛好農(nóng)歷新歷都在一起了。
    水漓閱讀 235評論 0 0
  • 經(jīng)濟(jì)師考試時(shí)間及專業(yè)科目介紹 2019年度經(jīng)濟(jì)專業(yè)技術(shù)資格考試于11月2、3日舉行。 經(jīng)濟(jì)專業(yè)技術(shù)資格考試設(shè)置兩個(gè)...
    愛恒信閱讀 192評論 0 0
  • 我常常不斷的去立一些小目標(biāo)。為什么會(huì)對這些小目標(biāo)呢?完全是隨意,這種隨性起意,注定是不長久的,總會(huì)因?yàn)橛龅竭@事那事...
  • 社會(huì)在不斷的進(jìn)步,人類文明在不斷的提高,越來越多的人希望利用自己的業(yè)余時(shí)間創(chuàng)造一份屬于自己的副業(yè),也許這種想法會(huì)讓...
    狗泉有闟膽閱讀 257評論 0 0
  • 今天也沒什么特殊感受,感覺吃不吃飯好像沒有特別大的差別,三天不吃飯已經(jīng)非常輕松了。早上99澳門磚,下午88小黃印,...
    kaffeefee閱讀 290評論 0 2

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