
概述
限流:網關上有大量請求,對指定服務進行限流,可以很大程度上提高服務的可用性與穩定性,限流的目的是通過對并發通路/請求進行限速,或對一個時間視窗内的請求進行限速來保護系統。一旦達到限制速率則可以拒絕服務、排隊或等待、降級。
在 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.
- 修改配置檔案
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 對象。
- 限流配置
這裡根據使用者 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());
}
- 完整的配置檔案:
@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 名額判斷是否需要限流。
最後
本文到此結束,感謝大家的閱讀。歡迎關注公衆号【當我遇上你】