概述
限流:網關上有大量請求,對指定服務進行限流,可以很大程度上提高服務的可用性與穩定性,限流的目的是通過對並發訪問/請求進行限速,或對一個時間窗口內的請求進行限速來保護系統。一旦達到限制速率則可以拒絕服務、排隊或等待、降級。
在 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 指標判斷是否需要限流。
最后
本文到此結束,感謝大家的閱讀。歡迎關注公眾號【當我遇上你】