SpringCloud 微服務閘道器 Gateway 元件

HuDu發表於2022-08-15

閘道器簡介

大家都都知道在微服務架構中,一個系統會被拆分為很多個微服務。那麼作為客戶端要如何去呼叫這麼多的微服務呢?如果沒有閘道器的存在,我們只能在客戶端記錄每個微服務的地址,然後分別去用。

這樣架構會存在很多問題:

  1. 每個業務都會需要鑑權、限流、許可權校驗、跨域等邏輯,如果每個業務都各自為戰,自己造輪子實現一遍,會很蛋疼,完全可以抽出來,放到一個統一的地方去做。
  2. 如果業務量比較簡單的話, 這種方式前期不會有什麼問題,但隨著業務越來越複雜,比如淘寶、亞馬遜,開啟一個頁面可能會涉及到數百個微服務協同工作,如果每一個微服務都分配一個域名的話,一方面客戶端程式碼會很難維護,涉及到數百個域名,另一方面是連線數的瓶頸,想象一 下你開啟一個APP,透過抓包發現涉及到了數百個遠端呼叫,這在移動端下會顯得非常低效。
  3. 後期如果需要對微服務進行重構的話, 也會變的非常麻煩,需要客戶端配合你一起進行改造,比如商品服務,隨著業務變的越來越複雜,後期需要進行拆分成多個微服務,這個時候對外提供的服務也需要拆分成多個,同時需要客戶端配合你進行改造,非常蛋疼。

上面的問題可以藉助 API 閘道器來解決。

注重穩定性:

  • 全域性性流控
  • 日誌統計
  • 防止 SQL 注入
  • 防止 Web 攻擊
  • 遮蔽工具掃描
  • 黑白 IP 名單
  • 證照/加解密處理

提供更好的服務

  • 服務級別流控
  • 服務降級與熔斷
  • 路由與負載均衡、灰度策略
  • 服務過濾、聚合與發現
  • 許可權驗證與使用者等級策略
  • 業務規則與引數校驗
  • 多級快取策略

所謂的API閘道器,就是指系統的統一入口,它封裝了應用程式的內部結構,為客戶端提供統一服務,一 些與業務本身功能無關的公共邏輯可以在這裡實現,諸如認證、鑑權、監控、路由轉發等等。新增上API閘道器之後,系統的架構圖變成了如下所示:

SpringCloud 微服務閘道器 Gateway 元件

一、什麼是 Spring Cloud Gateway

閘道器作為流量的入口,常用的功能包括路由轉發,許可權校驗,限流等。
Spring Cloud Gateway 是 Spring Cloud 官方推出的第二代閘道器框架,定位於取代 Netlix Zuul。相比Zuul 來說,Spring Cloud Gateway 提供更優秀的效能,更強大的有功能。
Spring Cloud Gateway 由 WebFlux + Netty + Reactor 實現的響應式的 API 閘道器。它不能在傳統的servlet 容器中工作,也不能構建成 war 包。
Spring Cloud Gateway 旨在為微服務架構提供一種簡單且有效的 API 路由的管理方式,並基於 Filter 的方式提供閘道器的基本功能,例如說安全認證、監控、限流等等。

其它閘道器元件:
在 SpringCloud 微服務體系中,有個很重要的元件就是閘道器,在 1.x 版本中都是採用的 Zuul 閘道器;但在2.x版本中,zuul 的升級一直跳票,SpringCloud 最後自 己研發了一個閘道器替代 Zuul,那就是SpringCloud Gateway
網上很多地方都說 Zuul 是阻塞的,Gateway是非阻塞的,這麼說是不嚴謹的,準確的講 zuul 1.x 是阻塞的,而在 2.x 的版本中,Zuul 也是基於 Netty,也是非阻塞的,如果一定要說效能,其實沒多大差距。

Spring Cloud Gateway 功能特徵

  • 基於Spring Framework 5,Project Reactor 和 Spring Boot 2.0 進行構建;
  • 動態路由:能夠匹配任何請求屬性;
  • 支援路徑重寫;
  • 整合Spring Cloud服務發現功能(Nacos、 Eruka);
  • 可整合流控降級功能(Sentinel、 Hystrix);
  • 可以對路由指定易於編寫的Predicate (斷言)和Filter (過濾器);

1.1、核心概念

  • 路由(route)
    路由是閘道器中最基礎的部分,路由資訊包括一個ID、一個目的URI、一組斷言工廠、一組 Filter 組成。如果斷言為真,則說明請求的 URL 和配置的路由匹配。

  • 斷言(predicates)
    Java8 中的斷言函式,SpringCloud Gateway 中的斷言函式型別是 Spring5.0 框架中的ServerWebExchange。斷言函式允許開發者去定義匹 Http request 中的任何資訊,比如請求頭和引數等。

  • 過濾器(Filter)
    SpringCloud Gateway 中的 filter 分為 Gateway Fller 和 Global Filter。Fiter 可以對請求和響應進行處理。

二、Spring Cloud Gateway 快速開始

2.1、環境搭建

2.1.1、引入依賴

<!--gateway 依賴-->
<dependency>
  <groupId>org.springframework.cloud</groupId>
 <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

2.1.2、配置 application.yml 檔案

現有兩個服務,order(訂單)和stock(庫存)服務

SpringCloud 微服務閘道器 Gateway 元件

server:
  port: 8088
spring:
  application:
    name: api-gateway
  cloud:
    # gateway 配置
    gateway:
      # 路由規則
      routes:
        # 路由唯一標識,路由到訂單
        - id: order_route
          # 需要轉發的地址
          uri: http://localhost:8020
          # 斷言規則,用於路由規則的匹配
          predicates:
            - Path=/order-server/**
            # http://localhost:8088/order-server/order/add 路由到
            # http://localhost:8020/order-server/order/add
          filters:
            # 轉發之前,去掉第一層的路徑
            # http://localhost:8020/order/add
            - StripPrefix=1
        #- id: stock_route

啟動閘道器服務,請求http://localhost:8088/order-server/order/add地址,可以看到路由到了訂單服務,並且請求庫存服務成功。

SpringCloud 微服務閘道器 Gateway 元件

2.2、整合 Nacos

現在是在配置檔案中寫死了轉發的路徑地址

2.2.1、引入依賴

<!--nacos 服務註冊與發現-->
<dependency>
  <groupId>com.alibaba.cloud</groupId>
 <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

2.2.2、編寫配置檔案

server:
  port: 8088
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.33.62:8847
        username: nacos
        password: nacos
    # gateway 配置
    gateway:
      # 路由規則
      routes:
        # 路由唯一標識,路由到訂單
        - id: order_route
          # lb 指的是從 nacos 中按照名稱獲取微服務,並遵循負載均衡策略
          uri: lb://order-server
          # 斷言規則,用於路由規則的匹配
          predicates:
            - Path=/order-server/**
          filters:
            - StripPrefix=1

可以看到 Nacos 註冊中心的服務

SpringCloud 微服務閘道器 Gateway 元件

此時請求介面http://localhost:8088/order-server/order/add

SpringCloud 微服務閘道器 Gateway 元件

配置簡寫

server:
  port: 8088
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.33.62:8847
        username: nacos
        password: nacos
    # gateway 配置
    gateway:
      discovery:
        locator:
          # 是否啟動自動識別 nacos 服務
          enabled: true

需要透過 nacos 中的服務名進行訪問http://localhost:8088/order-server/order/add

三、Gateway 路由斷言工廠(Route Predicate Factories)配置

作用:當請求 gateway 時候,使用斷言對請求進行匹配,如果匹配成功就路由轉發,匹配失敗就返回 404。

3.1、內建路由斷言工廠

官網地址

SpringCloud Gateway 包括許多內建的斷言工廠,所有這些斷言都與 HTTP 請求的不同屬性匹配。具體如下:

3.1.1、基於 Datetime 型別的斷言工廠

此型別的斷言,根據時間做判斷,主要有三個:
AfterRoutePredicateFactory:接收一個日期引數,判斷請求日期是否晚於指定日期
BeforeRoutePredicateFactory:接收一 個日期引數,判斷請求日期是否早於指定日期
BetweenRoutePredicateFactory:接收兩個日期引數,判斷請求日期是否在指定時間段內

時間型別為 ZonedDateTime.now()

- After=2022-08-12T15:00:00.000+08:00[Asia/Shanghai]
server:
  port: 8088
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.33.62:8847
        username: nacos
        password: nacos
    # gateway 配置
    gateway:
      # 路由規則
      routes:
        # 路由唯一標識,路由到訂單
        - id: order_route
          # lb 指的是從 nacos 中按照名稱獲取微服務,並遵循負載均衡策略
          uri: lb://order-server
          # 斷言規則,用於路由規則的匹配
          predicates:
            - Path=/order/**
            - After=2022-08-12T15:00:00.000+08:00[Asia/Shanghai]

請求成功

SpringCloud 微服務閘道器 Gateway 元件

修改時間為當前時間之前的時間後,訪問介面為 404

SpringCloud 微服務閘道器 Gateway 元件

3.1.2、基於遠端地址的斷言工廠

RemoteAddrRoutePredicateFactory:接收一個 IP 地址段,判斷請求主機地址是否在地址段中。

- RemoteAddr=192.168.1.1/24

3.1.3、基於 Cookie 的斷言工廠

CookieRoutePredicateFactory:接收兩個引數,cookie 名字和一個正規表示式。判斷請求 cookie 是否具有給定名稱且值與正規表示式匹配

- Cookie=chocolate,ch.

3.1.4、基於 Header 的斷言工廠

HeaderRoutePredicateFactory:接收兩個引數,標題名稱和正規表示式。判斷請求 Header 是否具有給定名稱且值與正規表示式匹配。

設定請求頭中,X-Request-Id,必須為數字

- Header=X-Request-Id,\d+

使用 postman 進行測試

SpringCloud 微服務閘道器 Gateway 元件

當請求頭中沒有X-Request-Id或者值不為數字時,訪問 404

SpringCloud 微服務閘道器 Gateway 元件

3.1.5、基於 Host 的斷言工廠

HostRoutePredicateFactory:接收一個引數,主機名模式。判斷請求的 Host 是否滿足匹配規則。

- Host=**.testhost.org

3.1.6、基於 Method 請求方法的斷言工廠

MethodRoutePredicateFactory:接收一個引數,判斷請求型別是否跟指定的型別匹配。

- Method=GET

當請求為 POST 請求時,404

SpringCloud 微服務閘道器 Gateway 元件

3.1.7、基於 Path 請求路徑的斷言工廠

PathRoutePredicateFactory:接收一個引數,判斷請求的 URI 部分是否滿足路徑規則。

# 效果類似於 url 中 /foo/xxx
- Path=/foo/{segment}

3.1.8、基於 Query 請求引數的斷言工廠

QueryRoutePredicateFactory:接收兩個引數,請求 param 和正規表示式,判斷請求引數是否具有給定名稱且值與正規表示式匹配。

# 效果類似於 url 中 ?baz=xxx
- Query=name,ba.

SpringCloud 微服務閘道器 Gateway 元件

3.1.9、基於路由權重的斷言工廠

WeightRoutePredicateFactory:接收一個[組名權重],然後對於同一個組內的路由按照權重轉發

routes:
  - id: weight_route1
    uri: host1
    predicates:
      - Path=/order/**
      - weitht=group3,1
  - id: weight_route2
    uri: host2
    predicates:
      - Path=/order/**
      - Weight=group3,9

3.2、自定義路由斷言工廠

自定義路由斷言工廠需要繼承 AbstractRoutePredicateFactory 類,重寫 apply 方法的邏輯。在 apply 方法中可以透過 exchange.getRequest() 拿到 ServerHttpRequest 物件,從而可以獲取到請求的引數、請求方式、請求頭等資訊。

注意:類命名必須以 RoutePredicateFactory 結尾

  1. 必須是 spring 元件 bean
  2. 類必須加上 RoutePredicateFactory 作為結尾
  3. 必須繼承 AbstractRoutePredicateFactory
  4. 必須宣告靜態內部類,宣告屬性來接收配置檔案中對應的斷言資訊
  5. 需要結合 shortcutFieldOrder 進行繫結
  6. 透過 apply 進行邏輯判斷,true 就是匹配成功,false 匹配失敗

實現效果為,當路由中有 hudu 字串時,透過

@Component
public class CheckAuthRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Config> {

    public CheckAuthRoutePredicateFactory() {
        super(Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("name");
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return new GatewayPredicate() {
            @Override
            public boolean test(ServerWebExchange exchange) {
                if (config.getName().equals("hudu")) {
                    return true;
                }
                return false;
            }
        };
    }

    /**
     * 用於接收配置檔案中 斷言的資訊
     */
    @Validated
    public static class Config {
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}

application.yml 配置

server:
  port: 8088
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.33.62:8847
        username: nacos
        password: nacos
    # gateway 配置
    gateway:
      # 路由規則
      routes:
        # 路由唯一標識,路由到訂單
        - id: order_route
          # lb 指的是從 nacos 中按照名稱獲取微服務,並遵循負載均衡策略
          uri: lb://order-server
          # 斷言規則,用於路由規則的匹配
          predicates:
            - Path=/order/**
            - CheckAuth=hudu

當 CheckAuth 不為 hudu 時,請求 404

四、過濾器工廠(GatewayFilter Factories)配置

Gateway 內建了很多的過濾器工廠,我們透過一些過濾器工廠可以進行一些業務邏輯處理器,比如新增剔除響應頭,新增去除引數等

4.1、內建過濾器

官方文件地址

SpringCloud 微服務閘道器 Gateway 元件

SpringCloud 微服務閘道器 Gateway 元件

4.1.1、新增請求頭

spring:
    # gateway 配置
    gateway:
      # 路由規則
      routes:
        # 路由唯一標識,路由到訂單
        - id: order_route
          # lb 指的是從 nacos 中按照名稱獲取微服務,並遵循負載均衡策略
          uri: lb://order-server
          # 斷言規則,用於路由規則的匹配
          predicates:
            - Path=/order/**
          filters:
            # 新增請求頭
            - AddRequestHeader=X-Request-color,red
@RequestMapping("/header")
public String header(@RequestHeader("X-Request-color") String color) {
  return color;
}

SpringCloud 微服務閘道器 Gateway 元件

4.1.2、新增請求引數

spring:
    # gateway 配置
    gateway:
      # 路由規則
      routes:
        # 路由唯一標識,路由到訂單
        - id: order_route
          # lb 指的是從 nacos 中按照名稱獲取微服務,並遵循負載均衡策略
          uri: lb://order-server
          # 斷言規則,用於路由規則的匹配
          predicates:
            - Path=/order/**
          filters:
            # 新增請求引數
            - AddRequestParameter=color,blue
@GetMapping("/parameter")
public String parameter(@RequestParam("color")String color) {
  return color;
}

SpringCloud 微服務閘道器 Gateway 元件

4.1.3、為匹配路由統一新增字首

filters:
  # 新增請求引數
  - PrefixPath=/order-server

效果等同於在遠處呼叫的服務上新增

server:
  servlet:
    context-path: order-server

請求介面http://localhost:8088/order/test實際是轉發到http://localhost:8088/order-server/order/test

4.1.4、重定向

filters:
  # 重定向
  - RedirectTo=302,https://www.baidu.com

請求介面http://localhost:8088/order/test

4.2、自定義過濾器工廠(區域性過濾器)

繼承 AbstractNameValueGatewayFilterFactory 且我們的自定義名稱必須要以 GatewayFilterFactory 結尾並交給 spring 管理。

實現效果當請求介面中帶有?name=hudu時,才能請求成功

@Component
public class CheckAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<CheckAuthGatewayFilterFactory.Config> {


    private static final Log log = LogFactory
            .getLog(CheckAuthGatewayFilterFactory.class);

    public CheckAuthGatewayFilterFactory() {
        super(CheckAuthGatewayFilterFactory.Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("key","value");
    }

    @Override
    public GatewayFilter apply(CheckAuthGatewayFilterFactory.Config config) {
        return (exchange, chain) -> {
            // 獲取 key value 值
            String key = exchange.getRequest().getQueryParams().getFirst(config.getKey());
            // 如果 key value 等於對應的值 成功
            if (StringUtils.hasText(key)) {
                if (config.getValue().equals(key)) {
                    return chain.filter(exchange);
                }
            }
            // 否則訪問 404
            exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
            return exchange.getResponse().setComplete();
        };
    }

    public static class Config {

        private String key;

        private String value;

        public String getKey() {
            return key;
        }

        public void setKey(String key) {
            this.key = key;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }
    }

}
filters:
  - CheckAuth=name,hudu

SpringCloud 微服務閘道器 Gateway 元件

4.3、全域性過濾器(Global Filters)配置

區域性過濾器和全域性過濾器的區別:
區域性過濾器:只針對某一個路由進行過濾,需要在路由中進行配置
全域性過濾器:針對所有路由請求,一旦定義了就會投入使用

SpringCloud 微服務閘道器 Gateway 元件

4.3.1、LoadBalancerClientFilter

LoadBalancerClientFilter 會檢視 exchange 的屬性 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的值(一個 URI),如果該值的 scheme 是 lb,比如:lb://myserver,它會使用 SpringCloud 的 LoadBalancerClient 來將 myservice 解析成實際的 host 和 port,並替換掉 ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR 的內容。
其實就是用來整合負載均衡 Ribbon 的

spring:
  cloud:
    gateway:
      routes:
        - id: order_route
          uri: lb://mall-order
          predicates:
            - Path=/order/**

4.3.2、自定義全域性過濾器

一般像授權,許可權認證,日誌記錄等,會使用全域性過濾器

@Component
public class MyGlobalFilter implements GlobalFilter {

    Logger log = LoggerFactory.getLogger(this.getClass());

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info(exchange.getRequest().getPath().value());
        return chain.filter(exchange);
    }
}

如果既定義了區域性過濾器,又定義了全域性過濾器,會先經過區域性過濾器,透過之後再經過全域性過濾器

五、請求日誌記錄&跨域處理

5.1、Reactor Netty 訪問日誌

要啟用 Reactor Netty 訪問日誌,需要設定-Dreactor.netty.http.server.accessLogEnabled=true
它必須是 Java 系統屬性,而不是 Spring Boot 屬性。

可以將日誌記錄為具有單獨的訪問日誌檔案。
logback.xml

<appender name="accessLog" class="ch.qos.logback.core.FileAppender">
  <file>access_log.log</file>
    <encoder>
    <pattern>%msg%n</pattern>
  </encoder>
</appender>

<appender name="async" class="ch.qos.logback.classic.AsyncAppender">
  <appender-ref ref="accessLog" />
</appender>

<logger name="reactor.netty.http.server.AccessLog" level="INFO" additivity="false">
  <appender-ref ref="async"/>
</logger>

SpringCloud 微服務閘道器 Gateway 元件

SpringCloud 微服務閘道器 Gateway 元件

六、Gateway 跨域配置(CORS Configuration)

透過 yml 配置方式

spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          # 允許跨域訪問的資源
          '[/**]':
            # 跨域允許來源
            allowedOrigins: "*"
            allowedMethods:
              - GET
              - POST
              - DELETE
              - PUT
              - OPTOIN

透過 javaconfig 配置方式

@Configuration
public class SystemCorsFilter {
    @Bean
    public CorsWebFilter corsWebFilter() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        // 允許的 method
        corsConfiguration.addAllowedMethod("*");
        // 允許的來源
        corsConfiguration.addAllowedOrigin("*");
        // 允許的請求頭
        corsConfiguration.addAllowedHeader("*");

        //由於是透過 webflux 的方式,需要額外新增此配置
        // 訪問的資源
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**",corsConfiguration);
        return new CorsWebFilter(source);
    }
}

七、Gateway 整合 Sentinel 限流

閘道器作為內部系統外的一層屏障,對內起到一定的保護作用,限流便是其中之一。閘道器層的限流可以簡單地針對不同路由進行限流,也可針對業務的介面進行限流,或者根據介面的特徵分組限流。

文件地址

  • 新增依賴
<!--sentinel 整合 gateway-->
<dependency>
  <groupId>com.alibaba.cloud</groupId>
 <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<!--sentinel 依賴-->
<dependency>
  <groupId>com.alibaba.cloud</groupId>
 <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
  • 新增配置
# 配置 sentinel
sentinel:
  transport:
    dashboard: 127.0.0.1:8858

7.1、控制檯實現方式

從1.6.0版本開始,Sentinel 提供了Spring Cloud Gateway 的適配模組,可以提供兩種資源維度的限流:

  1. route 維度:即在 Spring 配置檔案中配置的路由條目,資源名為對應的 routeld
  2. 自定義 API 維度:使用者可以利用 Sentinel 提供的 API 來自定義一些 API 分組

order-server 服務中提供如下服務

@RequestMapping("/hello")
public String hello() {
  System.out.println("下單成功!");
  return "Hello World ";
}

@RequestMapping("/flow")
  public String flow() {
  return "正常訪問";
}

@RequestMapping("/flowThread")
public String flowThread() throws InterruptedException {
  TimeUnit.SECONDS.sleep(2);
  return "正常訪問";
}

@RequestMapping("/get")
public String get() {
  return "查詢訂單";
}

@RequestMapping("/err")
public String err() {
  int i = 1 / 0;
  return "hello";
}

@RequestMapping("/get/{id}")
public String getById(@PathVariable("id")Integer id) {
  System.out.println("正常訪問");
  return "正常訪問";
}

啟動服務,可以看到 sentinel 已經為當前的路由生成了一個資源,設定流控 QPS 為 2,當請求閘道器頻繁時,閘道器被流控了。

SpringCloud 微服務閘道器 Gateway 元件

SpringCloud 微服務閘道器 Gateway 元件

SpringCloud 微服務閘道器 Gateway 元件

7.2、Gateway 整合 Sentinel 流控降級詳細配置

7.2.1、Burst size:突發請求額外允許數

當一秒請求三次以上時,才會進行限流

SpringCloud 微服務閘道器 Gateway 元件

7.2.2、針對斷言工廠進行限流

SpringCloud 微服務閘道器 Gateway 元件

精確:與設定的值相等
子串:模糊匹配
正則:正規表示式匹配

SpringCloud 微服務閘道器 Gateway 元件

注意,localhost訪問無效,需要透過 127.0.0.1 訪問才行

SpringCloud 微服務閘道器 Gateway 元件

針對請求頭中的 X-Request-Id 的值進行限流

SpringCloud 微服務閘道器 Gateway 元件

SpringCloud 微服務閘道器 Gateway 元件

其它配置可以自行測試。

7.2.3、根據詳細的地址分組流控

SpringCloud 微服務閘道器 Gateway 元件

現在就可以根據 api 分組進行流控

SpringCloud 微服務閘道器 Gateway 元件

SpringCloud 微服務閘道器 Gateway 元件

SpringCloud 微服務閘道器 Gateway 元件

7.2.4、降級

SpringCloud 微服務閘道器 Gateway 元件

7.3、自定義降級返回資訊

7.3.1、透過 yml

spring:
  cloud:
    sentinel:
      transport:
        dashboard: 127.0.0.1:8858
      scg:
        fallback:
          mode: response
          response-body: '{"code":403,"msg":"限流了"}'

SpringCloud 微服務閘道器 Gateway 元件

7.3.2、透過 GatewayCallbackManager

@Configuration
public class GatewayConfig {

    Logger log = LoggerFactory.getLogger(this.getClass());

    @PostConstruct
    public void init() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                // 列印異常
                log.error(throwable.getMessage());

                HashMap<String, String> map = new HashMap<>();
                map.put("code", HttpStatus.TOO_MANY_REQUESTS.toString());
                if (throwable instanceof FlowException) {
                    map.put("msg", "介面限流了");
                } else if (throwable instanceof DegradeException) {
                    map.put("msg", "服務降級了");
                } else if (throwable instanceof ParamFlowException) {
                    map.put("msg", "熱點引數限流了");
                } else if (throwable instanceof SystemBlockException) {
                    map.put("msg", "觸發系統保護規則了");
                } else if (throwable instanceof AuthorityException) {
                    map.put("msg", "授權規則不透過");
                }
                // 自定義異常處理
                return ServerResponse
                        // 響應狀態碼
                        .status(HttpStatus.OK)
                        // 響應型別
                        .contentType(MediaType.APPLICATION_JSON)
                        // 響應內容
                        .body(BodyInserters.fromValue(map));
            }
        };

        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}

SpringCloud 微服務閘道器 Gateway 元件

7.4、程式碼實現方式(瞭解)

使用者可以透過 GatewayRuleManage.loadRules(rules)手動載入閘道器規則
GatewayConfiguration 中新增

@Configuration
public class GatewayConfig {

    Logger log = LoggerFactory.getLogger(this.getClass());

    @PostConstruct
    public void doInit() {
        // 初始化自定義的 API
        initCustomizedApis();
        // 初始化閘道器限流規則
        initGatewayRules();
        // 自定義限流異常處理器
        initBlockRequestHandler();
    }

    private void initCustomizedApis() {
        HashSet<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition api = new ApiDefinition("user_service_api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    add(new ApiPathPredicateItem().setPattern("/user/**").setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        definitions.add(api);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

    private void initGatewayRules() {
        HashSet<GatewayFlowRule> rules = new HashSet<>();
        // resource:資源名稱,可以是閘道器中的 route 名稱,或者是自定義的 API 分組名稱
        // count:限流閾值
        // intervalSec:統計時間視窗,單位是 秒,預設是 1 秒
        rules.add(new GatewayFlowRule("order_route")
                .setCount(2)
                .setIntervalSec(1));
        rules.add(new GatewayFlowRule("user_service_api").setCount(2).setIntervalSec(1));
        // 載入閘道器規則
        GatewayRuleManager.loadRules(rules);
    }

    @PostConstruct
    public void initBlockRequestHandler() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                // 列印異常
                log.error(throwable.getMessage());

                HashMap<String, String> map = new HashMap<>();
                map.put("code", HttpStatus.TOO_MANY_REQUESTS.toString());
                if (throwable instanceof FlowException) {
                    map.put("msg", "介面限流了");
                } else if (throwable instanceof DegradeException) {
                    map.put("msg", "服務降級了");
                } else if (throwable instanceof ParamFlowException) {
                    map.put("msg", "熱點引數限流了");
                } else if (throwable instanceof SystemBlockException) {
                    map.put("msg", "觸發系統保護規則了");
                } else if (throwable instanceof AuthorityException) {
                    map.put("msg", "授權規則不透過");
                }
                // 自定義異常處理
                return ServerResponse
                        // 響應狀態碼
                        .status(HttpStatus.OK)
                        // 響應型別
                        .contentType(MediaType.APPLICATION_JSON)
                        // 響應內容
                        .body(BodyInserters.fromValue(map));
            }
        };

        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }
}

八、Gateway 閘道器高可用

為了保證 Gateway 的高可用性,可以同時啟動多個 Gateway 例項進行負載,在 Gateway 的上游使用Nginx 或者 F5 進行負載轉發以達到高可用。

SpringCloud 微服務閘道器 Gateway 元件

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章