spring-cloud-gateway限流


概述

限流:網關上有大量請求,對指定服務進行限流,可以很大程度上提高服務的可用性與穩定性,限流的目的是通過對並發訪問/請求進行限速,或對一個時間窗口內的請求進行限速來保護系統。一旦達到限制速率則可以拒絕服務、排隊或等待、降級。

在 Spring Cloud Gateway 中,有 Filter過濾器,因此可以在 pre 類型的 Filter 中自行實現上述三種過濾器。但是限流作為網關最基本的功能,Spring Cloud Gateway 官方就提供了 RequestRateLimiterGatewayFilterFactory 這個類,適用在 Redis 內的通過執行 Lua 腳本實現了令牌桶的方式。具體實現邏輯在 RequestRateLimiterGatewayFilterFactory 類中,lua 腳本在如下圖所示的文件夾中:

限流配置

應一小伙伴要求, 將 demo 升級到了最新版本

  • spring-cloud-dependencies: Hoxton.SR3
  • spring-boot-dependencies: 2.2.6.RELEASE

代碼放置在 github.

  1. 修改配置文件
server:
  port: 2000
spring:
  application:
    name: idc-gateway2
  redis:
    host: localhost
    port: 6379
    timeout: 6000ms  # 連接超時時長(毫秒)
    jedis:
      pool:
        max-active: 1000  # 連接池最大連接數(使用負值表示沒有限制)
        max-wait: -1ms      # 連接池最大阻塞等待時間(使用負值表示沒有限制)
        max-idle: 10      # 連接池中的最大空閑連接
        min-idle: 5       # 連接池中的最小空閑連接
  cloud:
    consul:
      host: localhost
      port: 8500
    gateway:
      discovery:
        locator:
          enabled: true # gateway可以通過開啟以下配置來打開根據服務的serviceId來匹配路由,默認是大寫
      routes:
        - id: provider1
          uri: lb://idc-provider1
          predicates:
            - Path=/p1/**
          filters:
            - StripPrefix=1
            - name: RequestRateLimiter
              args:
                key-resolver: '#{@ipKeyResolver}'
                redis-rate-limiter.replenishRate: 1
                redis-rate-limiter.burstCapacity: 3
        - id: provider2
          uri: lb://idc-provider2
          predicates:
            - Path=/p2/**
          filters:
            - StripPrefix=1

在上面的配置文件,指定程序的端口為 2000,配置了 redis 的信息,並配置了 RequestRateLimiter 的限流過濾器,該過濾器需要配置三個參數:

  • burstCapacity,令牌桶總容量。
  • replenishRate,令牌桶每秒填充平均速率。
  • key-resolver,用於限流的鍵的解析器的 Bean 對象的名字。它使用 SpEL 表達式根據#{@beanName}從 Spring 容器中獲取 Bean 對象。
  1. 限流配置

這里根據用戶 ID 限流,請求路徑中必須攜帶 userId 參數

@Bean
KeyResolver userKeyResolver() {
  return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
}

KeyResolver 需要實現 resolve 方法,比如根據 userid 進行限流,則需要用 userid 去判斷。實現完 KeyResolver 之后,需要將這個類的 Bean 注冊到 Ioc 容器中。

如果需要根據 IP 限流,定義的獲取限流 Key 的 bean 為:

@Primary
@Bean
KeyResolver ipKeyResolver() {
  return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}

通過 exchange 對象可以獲取到請求信息,這邊用了 HostName,如果你想根據用戶來做限流的話這邊可以獲取當前請求的用戶 ID 或者用戶名就可以了,比如:

如果需要根據接口的 URI 進行限流,則需要獲取請求地址的 uri 作為限流 key,定義的 Bean 對象為:

@Bean
KeyResolver apiKeyResolver() {
  return exchange -> Mono.just(exchange.getRequest().getPath().value());
}
  1. 完整的配置文件:
@Component
public class RateLimitConfig {
    @Bean
    KeyResolver userKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
    }

    @Primary
    @Bean
    KeyResolver ipKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
    }

    @Bean
    KeyResolver apiKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getPath().value());
    }
}

限流演練

這里我們用 postman 進行測試

基於系統負載的動態限流

在實際工作中,我們可能還需要根據網絡連接數、網絡流量、CPU 或內存負載等來進行動態限流。在這里我們以 CPU 為栗子。

我們需要借助 Spring Boot Actuator 提供的 Metrics 能力進行實現基於 CPU 的限流 —— 當 CPU 使用率高於某個閾值就開啟限流,否則不開啟限流。

我們在項目中引入 Actuator 的依賴坐標

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

然后基於過濾器和 metrics 指標判斷是否需要限流。

最后

本文到此結束,感謝大家的閱讀。歡迎關注公眾號【當我遇上你】


免責聲明!

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



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