Alibaba微服務組件 - Gateway(二) Spring Cloud Gateway快速開始


2.1 環境搭建

2.1.1 引入依賴

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

2.1.2 編寫yml配置文件

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 網關gateway配置
    gateway:
      # 路由配置
      routes:
        - id: order_route  # 路由的唯一標識,路由到order
          uri: http://localhost:8020  # 需要轉發的地址
          # 斷言規則,用於路由規則的匹配
          predicates:
            # 因為是字符串所以可以這樣寫,自動映射
            # http://localhost:8088/order-serv/order/add 路由到↓
            # http://localhost:8020/order-serv/order/add
            - Path=/order-serv/**
          # 過濾器,請求在傳遞過程中可以通過過濾器對其進行一定的修改
          filters:
            - StripPrefix=1 # 轉發之前去掉1層路徑(內置的一種過濾器) 變成http://localhost:8020/order/add

2.1.3 集成Nacos

引入依賴

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

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

編寫yml配置文件

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 網關gateway配置
    gateway:
      # 路由配置
      routes:
        - id: order_route  # 路由的唯一標識,路由到order
          uri: lb://order-server  # 需要轉發的地址  lb:使用nacos中本地的負載均衡策略 order-server 服務名
          # 斷言規則,用於路由規則的匹配
          predicates:
            # 因為是字符串所以可以這樣寫,自動映射
            # http://localhost:8088/order-serv/order/add 路由到↓
            # http://localhost:8020/order-serv/order/add
            - Path=/order-serv/**
          # 過濾器,請求在傳遞過程中可以通過過濾器對其進行一定的修改
          filters:
            - StripPrefix=1 # 轉發之前去掉1層路徑(內置的一種過濾器) 變成http://localhost:8020/order/add
    # 注冊到nacos中
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      discovery:
        namespace: 6325e312-7f95-48d7-8d06-810047ed3956

簡寫: 去掉關於路由的配置,自動尋找服務

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 網關gateway配置
    gateway:
      discovery:
        locator:
          enabled: true # 是否啟動我們的自動識別nacos服務  可以直接用服務名作為前綴訪問,斷言也是根據服務名做斷言路由到我們的真實服務地址
                        # http://localhost:8088/order-server/order/add

    # 注冊到nacos中
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      discovery:
        namespace: 6325e312-7f95-48d7-8d06-810047ed3956

測試(這時候,就發現只要按照網關地址/微服務/接口的格式去訪問,就可以得到成功響應)
image

2.2 路由斷言工廠(Route Predicate Factories)配置(局部,只針對某一個路由)

作用: 當請求gateway的時候,  使用斷言對請求進行匹配, 如果匹配成功就路由轉發, 如果匹配失敗就返回404
類型:內置,自定義

2.2.1 內置斷言工廠

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

  • 基於Datetime類型的斷言工廠
    此類型的斷言根據時間做判斷,主要有三個:
    AfterRoutePredicateFactory: 接收一個日期參數,判斷請求日期是否晚於指定日期
    BeforeRoutePredicateFactory: 接收一個日期參數,判斷請求日期是否早於指定日期
    BetweenRoutePredicateFactory: 接收兩個日期參數,判斷請求日期是否在指定時間段內
    ZonedDateTime.now()
spring:
 cloud:
   gateway:
     routes:
     - id: after_route
       uri: https://example.org
       predicates:
       - After=2017-01-20T17:42:47.789-07:00[America/Denver]
  • 基於遠程地址的斷言工廠
    RemoteAddrRoutePredicateFactory:接收一個IP地址段,判斷請求主機地址是否在地址段中
    ‐ RemoteAddr=192.168.1.1/24
  • 基於Cookie的斷言工廠
    CookieRoutePredicateFactory:接收兩個參數,cookie 名字和一個正則表達式。 判斷請求
    cookie是否具有給定名稱且值與正則表達式匹配。
    ‐ Cookie=chocolate, ch.
  • 基於Header的斷言工廠
    HeaderRoutePredicateFactory:接收兩個參數,標題名稱和正則表達式。  判斷請求Header是否具有給定名稱且值與正則表達式匹配。
    ‐ Header=X‐Request‐Id, \d+
  • 基於Host的斷言工廠
    HostRoutePredicateFactory:接收一個參數,主機名模式。判斷請求的Host是否滿足匹配規則。
    ‐ Host=**.testhost.org
  • 基於Method請求方法的斷言工廠
    MethodRoutePredicateFactory:接收一個參數,判斷請求類型是否跟指定的類型匹配。
    ‐ Method=GET
  • 基於Path請求路徑的斷言工廠
    PathRoutePredicateFactory:接收一個參數,判斷請求的URI部分是否滿足路徑規則。
    ‐ Path=/foo/{segment}
  • 基於Query請求參數的斷言工廠
    QueryRoutePredicateFactory :接收兩個參數,請求param和正則表達式, 判斷請求參數是否具有給定名稱且值與正則表達式匹配。
    - Query=baz, ba.
  • 基於路由權重的斷言工廠
    WeightRoutePredicateFactory:接收一個[組名,權重], 然后對於同一個組內的路由按照權重轉發
spring:
 cloud:
   gateway:
     routes:
     - id: weight_high
       uri: https://weighthigh.org
       predicates:
       - Weight=group1, 8
     - id: weight_low
       uri: https://weightlow.org
       predicates:
       - Weight=group1, 2

2.2.2 自定義斷言工廠

1. 必須spring組件 注入bean(@Component)
2. 類必須加上RoutePredicateFactory作為結尾
3. 必須繼承AbstractRoutePredicateFactory
4. 必須聲明靜態內部類   聲明屬性來接收 配置文件中對應的斷言的信息
5. 需要結合shortcutFieldOrder進行綁定
6. 通過apply進行邏輯判斷  true就是匹配成功   false匹配失敗

注意: 命名需要以 RoutePredicateFactory 結尾,必須注入到spring中

package com.xiexie.gateway.predicate;

import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;

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

/**
 * @Description 自定義斷言工廠
 * @Date 2022-04-19 13:49
 * @Author xie
 */
@Component
public class CheckAuthRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Config> {

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

    @Override
    public List<String> shortcutFieldOrder() {
        // 必須跟config中的屬性名進行綁定
        return Arrays.asList("name");
    }

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

            @Override
            public String toString() {
                return String.format("CheckAuth: name=%s", config.getName());
            }
        };
    }

    /**
     * 用於接收配置文件中 斷言的信息
     */
    @Validated
    public static class Config {

        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}
server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 網關gateway配置
    gateway:
      # 路由配置
      routes:
        - id: order_route  # 路由的唯一標識,路由到order
          uri: lb://order-server  # 需要轉發的地址  lb:使用nacos中本地的負載均衡策略 order-server 服務名
          # 斷言規則,用於路由規則的匹配
          predicates:
            # 因為是字符串所以可以這樣寫,自動映射
            # http://localhost:8088/order-serv/order/add 路由到↓
            # http://localhost:8020/order-serv/order/add
            - Path=/order-serv/**
            # 自定義CheckAuth斷言工廠
            - CheckAuth=xiexie
          # 過濾器,請求在傳遞過程中可以通過過濾器對其進行一定的修改
          filters:
            - StripPrefix=1 # 轉發之前去掉1層路徑(內置的一種過濾器) 變成http://localhost:8020/order/add
    # 注冊到nacos中
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      discovery:
        namespace: 6325e312-7f95-48d7-8d06-810047ed3956

2.3 過濾器工廠( GatewayFilter Factories)配置(局部,只針對某一個路由)

Gateway 內置了很多的過濾器工廠,我們通過一些過濾器工廠可以進行一些業務邏輯處理器,比如添加剔除響應頭,添加去除參數等
image

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

2.3.1 內置過濾器工廠

過濾器工廠 作用 參數
AddRequestHeader 為原始請求添加Header Header的名稱及值
AddRequestParameter 為原始請求添加請求參數 參數名稱及值
AddResponseHeader 為原始響應添加Header Header的名稱及值
DedupeResponseHeader 剔除響應頭中重復的值 需要去重的Header名稱及去重策略
Hystrix 為路由引入Hystrix的斷路器保護 HystrixCommand的名稱
FallbackHeaders 為fallbackUri的請求頭中添加具體的異常信息 Header的名稱
PrefixPath 為原始請求路徑添加前綴 前綴路徑
PreserveHostHeader 為請求添加一個preserveHostHeader=true 的屬性,路由過濾器會檢查該屬性以決定是否要發送原始的Host
RequestRateLimiter 用於對請求限流,限流算法為令牌桶 keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus
RedirectTo 將原始請求重定向到指定的URL http狀態碼及重定向的url
RemoveHopByHopHeadersFilter 為原始請求刪除IETF組織規定的一系列Header 默認就會啟用,可以通過配置指定僅刪除哪些Header
RemoveRequestHeader 為原始請求刪除某個Header Header名稱
RemoveResponseHeader 為原始響應刪除某個Header Header名稱
RewritePath 重寫原始的請求路徑 原始路徑正則表達式以及重寫后路徑的正則表達式
RewriteResponseHeader 重寫原始響應中的某個Header Header名稱、值的正則表達式,重寫后的值
SaveSession 在轉發請求之前,強制執行WebSession::save操作
secureHeaders 為原始響應添加一系列起安全作用的響應頭 無,支持修改這些安全響應頭的值
SetPath 修改原始的請求路徑 修改后的路徑
SetResponseHeader 修改原始響應中某個Header的值 Header名稱,修改后的值
SetStatus 修改原始響應的狀態碼 HTTP 狀態碼,可以是數字,也可以是字符串
StripPrefix 用於截斷原始請求的路徑 使用數字表示要截斷的路徑的數量
Retry 針對不同的響應進行重試 retries、statuses、methods、series
RequestSize 設置允許接收最大請求包的大 小。如果請求包大小超過設置的值,則返回 413 Payload Too Large 請求包大小,單位為字節,默認值為5M
ModifyRequestBody 在轉發請求之前修改原始請求體內容 修改后的請求體內容
ModifyResponseBody 修改原始響應體的內容 修改后的響應體內容

2.3.1.1 添加請求頭(上下文參考-全量配置文件)

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 網關gateway配置
    gateway:
      # 路由配置
      routes:
        - id: order_route  # 路由的唯一標識,路由到order
          uri: lb://order-server  # 需要轉發的地址  lb:使用nacos中本地的負載均衡策略(loadbalancer) order-server 服務名
          # 斷言規則,用於路由規則的匹配
          predicates:
            # 因為是字符串所以可以這樣寫,自動映射
            # http://localhost:8088/order-serv/order/add 路由到↓
            # http://localhost:8020/order-serv/order/add   http://order-server/order-serv/order/add
            - Path=/order-serv/**
            # 自定義CheckAuth斷言工廠
            - CheckAuth=xiexie # 數據放在自定義斷言工廠的config中,根據config映射到的數據值進行判斷是否匹配到
          # 過濾器,請求在傳遞過程中可以通過過濾器對其進行一定的修改
          filters:
            - StripPrefix=1 # 轉發之前去掉1層路徑(內置的一種過濾器) 變成http://localhost:8020/order/add
            # 其他的內置過濾器
            - AddRequestHeader=X-Request-color, red # 添加請求頭參數
            # - AddRequestParameter=color, blue # 添加請求參數
            # - PrefixPath=/mall‐order # 添加前綴 對應微服務需要配置context‐path: /mall‐order
            # - RedirectTo=302, https://www.baidu.com/ # 重定向到百度
            # - CheckAuth=name, xiexie # 數據放在自定義的過濾器中,根據config映射到的數據進行判斷是否放行
    # 注冊到nacos中
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      discovery:
        namespace: 6325e312-7f95-48d7-8d06-810047ed3956

@RequestMapping("/header")
public String header(@RequestHeader("X-Request-color") String color) {
    System.out.println("gateWay獲取請求頭X‐Request‐color:" + color);
    return color;
}

2.3.1.2 添加請求參數(上下文參考上文全量配置文件)

- AddRequestParameter=color, blue # 添加請求參數
@RequestMapping("/parameter")
public String parameter(@RequestParam("color") String color) {
    System.out.println("gateWay獲取參數color:" + color);
    return color;
}

2.3.1.3 為匹配的路由統一添加前綴(上下文參考上文全量配置文件)

- PrefixPath=/mall‐order # 添加前綴 對應微服務需要配置context‐path: /mall‐order
server:
  port: 8020
  servlet:
    context-path: /mall‐order

2.3.1.4 重定向操作(上下文參考上文全量配置文件)

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

.....

.....

還有很多,自行參照上面列表測試

2.3.2 自定義過濾器工廠

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

跟自定義的斷言工廠相似

實現過濾工廠

package com.xiexie.gateway.filter;

import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.factory.RedirectToGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;
import java.util.Set;

/**
 * @Description
 * @Date 2022-04-19 15:37
 * @Author xie
 */
@Component
public class CheckAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<CheckAuthGatewayFilterFactory.Config> {

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

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

    @Override
    public GatewayFilter apply(CheckAuthGatewayFilterFactory.Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
                Set<String> stringSet = queryParams.keySet();
                if (stringSet.contains(config.getParam()) && config.getValue().equals(queryParams.get(config.getParam()).get(0))) {
                    // 正常請求
                    return chain.filter(exchange);
                } else {
                    // 返回404並結束
                    exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
                    return exchange.getResponse().setComplete();
                }
            }
        };
    }

    public static class Config {

        private String param;

        private String value;

        public String getParam() {
            return param;
        }

        public void setParam(String param) {
            this.param = param;
        }

        public String getValue() {
            return value;
        }

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

配置文件中配置(上下文參考上文全量配置文件)

- CheckAuth=name, xiexie # 數據放在自定義的過濾器中,根據config映射到的數據進行判斷是否放行

2.4 全局過濾器(Global Filters)配置

image

局部過濾器和全局過濾器區別:

  • 局部:局部針對某個路由, 需要在路由中進行配置
  • 全局:針對所有路由請求, 一旦定義就會投入使用
    GlobalFilter 接口和 GatewayFilter 有一樣的接口定義,只不過, GlobalFilter 會作用於所有路由

2.4.1 自定義全局過濾器

package com.xiexie.gateway.filter.global;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @Description 自定義全局過濾器
 * @Date 2022-04-19 16:30
 * @Author xie
 */
@Component
public class GlobalLogFilter implements GlobalFilter {

    Logger log = LoggerFactory.getLogger(GlobalLogFilter.class);

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        log.info(exchange.getRequest().getPath().value());
        return chain.filter(exchange);
    }
}

2.4.2 Reactor Netty 訪問日志(gateway自動定義的日志收集)

https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#reactor-netty-access-logs
要啟用 Reactor Netty 訪問日志,請設置 -Dreactor.netty.http.server.accessLogEnabled=true

它必須是 Java 系統屬性(設置JVM環境變量的),而不是 Spring Boot 屬性。
java -jar xxxGatewat.jar -Dreactor.netty.http.server.accessLogEnabled=true

可以將日志記錄系統配置為具有單獨的訪問日志文件。(比如配置在Logback)

2.5 Gateway跨域配置(CORS Configuration)

image

通過yml配置的方式
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#cors-configuration

2.5.1 通過yml配置的方式

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 網關gateway配置
    gateway:
      # 跨域配置
      globalcors:
        cors-configurations:
          '[/**]':  # 允許跨域訪問的資源
            allowedOrigins: "*"  # 跨域允許的來源
            allowedMethods:  # 跨域允許的method
              - GET
              - POST
              - DELETE
              - PUT
              - OPTION

2.5.1 通過java代碼方式

package com.xiexie.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;

/**
 * @Description 跨域配置
 * @Date 2022-04-19 17:06
 * @Author xie
 */
@Configuration
public class CorsConfig {

    @Bean
    public CorsWebFilter corsWebFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*"); // 允許跨域的method
        config.addAllowedOrigin("*"); // 允許跨域的來源
        config.addAllowedHeader("*"); // 允許跨域的header頭

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config); // 允許跨域訪問的資源
        return new CorsWebFilter(source);
    }
}

2.6 gateway整合sentinel流控降級

網關作為內部系統外的一層屏障, 對內起到一定的保護作用, 限流便是其中之一. 網關層的限流可以簡單地針對不同路由進行限流, 也可針對業務的接口進行限流,或者根據接口的特征分組限流。
官方文檔:https://github.com/alibaba/Sentinel/wiki/網關限流
image

Sentinel 1.6.3 引入了網關流控控制台的支持,用戶可以直接在 Sentinel 控制台上查看 API Gateway 實時的 route 和自定義 API 分組監控,管理網關規則和 API 分組配置。

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

  • route 維度:即在 Spring 配置文件中配置的路由條目,資源名為對應的 routeId
  • 自定義 API 維度:用戶可以利用 Sentinel 提供的 API 來自定義一些 API 分組

Sentinel 1.6.0 引入了 Sentinel API Gateway Adapter Common 模塊,此模塊中包含網關限流的規則和自定義 API 的實體和管理邏輯:

  • GatewayFlowRule:網關限流規則,針對 API Gateway 的場景定制的限流規則,可以針對不同 route 或自定義的 API 分組進行限流,支持針對請求中的參數、Header、來源 IP 等進行定制化的限流。
  • ApiDefinition:用戶自定義的 API 定義分組,可以看做是一些 URL 匹配的組合。比如我們可以定義一個 API 叫 my_api,請求 path 模式為 /foo/** 和 /baz/** 的都歸到 my_api 這個 API 分組下面。限流的時候可以針對這個自定義的 API 分組維度進行限流。

2.6.1 添加依賴

<!--gateway 整合sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

2.6.2 添加配置

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 整合sentinel
    sentinel:
      transport:
        # 添加sentinel控制台
        dashboard: 127.0.0.1:8858

image

具體操作可參考sentinel章節操作詳解

2.6.3 自定義異常

2.6.3.1 通過配置完成

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 整合sentinel
    sentinel:
      transport:
        # 添加sentinel控制台
        dashboard: 127.0.0.1:8858
      # 自定義異常
      scg:
        fallback:
          mode: response
          response-body: '{"code":"403", "msg":"限流了"}'

2.6.3.2 通過GatewayCallbackManager(代碼實現)

package com.xiexie.gateway.config;

import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description 自定義限流異常
 * @Date 2022-04-20 9:19
 * @Author xie
 */
@Configuration
public class GatewaySentinelConfig {

    @PostConstruct
    public void init() {

        BlockRequestHandler blockRequestHandler = (serverWebExchange, throwable) -> {

            // 自定義異常處理
            Map<String, String> result = new HashMap<>();

            if (throwable instanceof FlowException) {
                // 限流

            } else if (throwable instanceof DegradeException) {
                // 降級

            } // .......

            result.put("code", String.valueOf(HttpStatus.TOO_MANY_REQUESTS.value()));
            result.put("code", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());

            return ServerResponse.status(HttpStatus.OK)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(BodyInserters.fromValue(result));
        };
    }
}

2.6.3 代碼實現限流降級操作

用戶可以通過 GatewayRuleManager.loadRules(rules) 手動加載網關規則 GatewayConfiguration中添加:

package com.xiexie.gateway.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @Description 自定義網關限流降級和異常
 * @Date 2022-04-20 9:19
 * @Author xie
 */
@Configuration
public class GatewaySentinelConfig {

    @PostConstruct
    public void init() {

        // 自定義異常信息
        initBlockRequestHandler();

        // 自定義網關限流規則
        initGatewayRules();
    }

    private void initBlockRequestHandler() {
        BlockRequestHandler blockRequestHandler = (serverWebExchange, throwable) -> {

            // 自定義異常處理
            Map<String, String> result = new HashMap<>();

            if (throwable instanceof FlowException) {
                // 限流

            } else if (throwable instanceof DegradeException) {
                // 降級

            } // .......

            result.put("code", String.valueOf(HttpStatus.TOO_MANY_REQUESTS.value()));
            result.put("code", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());

            return ServerResponse.status(HttpStatus.OK)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(BodyInserters.fromValue(result));
        };
    }

    private void initGatewayRules() {

        Set<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_server_api").setCount(2).setIntervalSec(1));

        // 加載網關規則
        GatewayRuleManager.loadRules(rules);
    }
}

其中網關限流規則 GatewayFlowRule 的字段解釋如下:

字段 解釋
resource 資源名稱,可以是網關中的 route 名稱或者用戶自定義的 API 分組名稱。
resourceMode 規則是針對 API Gateway 的 route(RESOURCE_MODE_ROUTE_ID)還是用戶在 Sentinel 中定義的 API 分組(RESOURCE_MODE_CUSTOM_API_NAME),默認是 route。
grade 限流指標維度,同限流規則的 grade 字段。
count 限流閾值
intervalSec 統計時間窗口,單位是秒,默認是 1 秒。
controlBehavior 流量整形的控制效果,同限流規則的 controlBehavior 字段,目前支持快速失敗和勻速排隊兩種模式,默認是快速失敗。
burst 應對突發請求時額外允許的請求數目。
maxQueueingTimeoutMs 勻速排隊模式下的最長排隊時間,單位是毫秒,僅在勻速排隊模式下生效。
paramItem 參數限流配置。若不提供,則代表不針對參數進行限流,該網關規則將會被轉換成普通流控規則;否則會轉換成熱點規則。
paramItem - parseStrategy 從請求中提取參數的策略,目前支持提取來源 IP(PARAM_PARSE_STRATEGY_CLIENT_IP)、Host(PARAM_PARSE_STRATEGY_HOST)、任意 Header(PARAM_PARSE_STRATEGY_HEADER)和任意 URL 參數(PARAM_PARSE_STRATEGY_URL_PARAM)四種模式。
paramItem - fieldName 若提取策略選擇 Header 模式或 URL 參數模式,則需要指定對應的 header 名稱或 URL 參數名稱。
paramItem - pattern 參數值的匹配模式,只有匹配該模式的請求屬性值會納入統計和流控;若為空則統計該請求屬性的所有值。(1.6.2 版本開始支持)
paramItem - matchStrategy 參數值的匹配策略,目前支持精確匹配(PARAM_MATCH_STRATEGY_EXACT)、子串匹配(PARAM_MATCH_STRATEGY_CONTAINS)和正則匹配(PARAM_MATCH_STRATEGY_REGEX)。(1.6.2 版本開始支持)

用戶可以通過 GatewayRuleManager.loadRules(rules) 手動加載網關規則,或通過 GatewayRuleManager.register2Property(property) 注冊動態規則源動態推送(推薦方式)

2.7 網關高可用

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

image


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM