天天看點

Sentinel網關限流 與Spring Cloud Gateway 整合

網關如何限流?

Spring Cloud Gateway本身自帶的限流實作,過濾器是RequestRateLimiterGatewayFilterFactory,不過這種比較簡單,有興趣的可以實作下。

今天的重點是內建阿裡的Sentinel實作網關限流,sentinel顧名思義:衛兵;在Redis中叫做哨兵,用于監控主從切換,但是在微服務中叫做流量防衛兵。Sentinel 以流量為切入點,從流量控制、熔斷降級、系統負載保護等多個次元保護服務的穩定性。

Sentinel 具有以下特征:

  • 豐富的應用場景:Sentinel 承接了阿裡巴巴近 10 年的雙十一大促流量的核心場景,例如秒殺(即突發流量控制在系統容量可以承受的範圍)、消息削峰填谷、叢集流量控制、實時熔斷下遊不可用應用等。
  • 完備的實時監控:Sentinel 同時提供實時的監控功能。您可以在控制台中看到接入應用的單台機器秒 級資料,甚至 500 台以下規模的叢集的彙總運作情況。
  • 廣泛的開源生态:Sentinel 提供開箱即用的與其它開源架構/庫的整合子產品,例如與 Spring Cloud、Apache Dubbo、gRPC、Quarkus 的整合。您隻需要引入相應的依賴并進行簡單的配置即可快速地接入 Sentinel。同時 Sentinel 提供 Java/Go/C++ 等多語言的原生實作。
  • 完善的 SPI 擴充機制:Sentinel 提供簡單易用、完善的 SPI 擴充接口。您可以通過實作擴充接口來快速地定制邏輯。例如定制規則管理、适配動态資料源等。

Sentinel 的主要特性如下圖:

Sentinel網關限流 與Spring Cloud Gateway 整合

從1.6.0版本開始,Sentinel提供了SpringCloud Gateway的适配子產品,可以提供兩種資源次元的限流:

**route次元:**即在配置檔案中配置的路由條目,資源名為對應的routeId,這種屬于粗粒度的限流,一般是對某個微服務進行限流。

**自定義API次元:**使用者可以利用Sentinel提供的API來自定義一些API分組,這種屬于細粒度的限流,針對某一類的uri進行比對限流,可以跨多個微服務。

項目實踐

建立一個gateway-service子產品,添加如下依賴:

<!--nacos注冊中心-->
    <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>

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

    <!--    spring cloud gateway整合sentinel的依賴-->
    <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>      

配置檔案

配置檔案中主要指定以下三種配置:

  • nacos的位址
  • sentinel控制台的位址
  • 網關路由的配置

配置如下:

server:
  port: 8100
logging:
  path: d:/
spring:
  application:
    name: gateway-service
  cloud:
    nacos:
      server-addr: localhost:8848
    sentinel:
      transport:
        dashboard: localhost:8080
    gateway:
      routes:
        - id: mqtt-service-route
          uri: lb://mqtt-service
          predicates:
            - Path=/mqtt/**
        - id: usermgr-service-route
          uri: lb://usermgr-service
          predicates:
            - Path=/usermgr/**      

上述配置中設定了一個路由usermgr-service-route,隻要請求路徑滿足http://127.0.0.1:8100/usermgr/user/id都會被路由到usermgr-service這個服務中

限流配置

經過上述兩個步驟其實已經整合好了Sentinel,此時通路一下接口:http://127.0.0.1:8100/usermgr/user/id

然後在sentinel控制台可以看到已經被監控了,監控的路由是usermgr-service,如下圖:

Sentinel網關限流 與Spring Cloud Gateway 整合

上圖中對usermgr-service這個路由做出了限流,QPS門檻值為1。此時快速通路:http://127.0.0.1:8100/usermgr/user/id,看到已經被限流了,如下圖:

Sentinel網關限流 與Spring Cloud Gateway 整合

以上route次元的限流已經配置成功,小夥伴可以自己照着上述步驟嘗試一下。

API分組限流

首先需要定義一個分組,API管理-> 新增API分組,如下圖:

Sentinel網關限流 與Spring Cloud Gateway 整合

比對模式選擇了精确比對(還有字首比對,正則比對),是以隻有這個uri:http://127.0.0.1:8100/usermgr/user/id會被限流。

第二步需要對這個分組添加流控規則,流控規則->新增網關流控,如下圖

Sentinel網關限流 與Spring Cloud Gateway 整合

API名稱那裡選擇對應的分組即可,新增之後,限流規則就生效了。

如何自定義限流異常資訊?

從上面的示範中可以看到預設的異常傳回資訊是:“Block…”,這種肯定是用戶端不能接受的,是以需要定制自己的異常傳回資訊。

下面介紹兩種不同的方式定制異常傳回資訊,開發中自己選擇其中一種。

直接配置檔案中定制

開發者可以直接在配置檔案中直接修改傳回資訊,配置如下:

spring:
  cloud:
    ## 整合sentinel,配置sentinel控制台的位址
    sentinel:
      #配置限流之後,響應内容
      scg:
        fallback:
          ## 兩種模式,一種是response傳回文字提示資訊,
          ## 一種是redirect,重定向跳轉,需要同時配置redirect(跳轉的uri)
          mode: response
          ## 響應的狀态
          response-status: 200
          ## 響應體
          response-body: '{"code": 200,"message": "請求失敗,稍後重試!"}'      

上述配置中mode配置的是response,一旦被限流了,将會傳回JSON串

{
    "code": 200,
    "message": "請求失敗,稍後重試!"
}      

重定向的配置如下:

spring:
  cloud:
    ## 整合sentinel,配置sentinel控制台的位址
    sentinel:
      #配置限流之後,響應内容
      scg:
        fallback:
          ## 兩種模式,一種是response傳回文字提示資訊,一種是redirect,重定向跳轉,需要同時配置redirect(跳轉的uri)
          mode: redirect
          ## 跳轉的URL
          redirect: http://www.baidu.com      

一旦被限流,将會直接跳轉到:http://www.baidu.com

編碼定制

package com.spacetime.gateway.config;

import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
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;

@Configuration
public class GatewayConfig {
    /**
     * 自定義限流處理器
     */
    @PostConstruct
    public void initBlockHandlers() {
        BlockRequestHandler blockHandler = (serverWebExchange, throwable) -> {
            Map map = new HashMap();
            map.put("code",200);
            map.put("message","請求失敗,稍後重試!");
            return ServerResponse.status(HttpStatus.OK)
                    .contentType(MediaType.APPLICATION_JSON_UTF8)
                    .body(BodyInserters.fromObject(map));
        };
        GatewayCallbackManager.setBlockHandler(blockHandler);
    }
}      

總結