(1). Filter RequestRateLimiterGatewayFilterFactory
Spring Cloud Gateway提供了RequestRateLimiter限流,它依赖:Redis和Lua脚本.
(2). 添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
(3). application.yml
#端口
server:
port: 9000
spring:
application:
name: gateway-server # 应用名称
redis:
timeout: 10000
host: 127.0.0.1
port: 6379
database: 0
lettuce:
pool:
max-active: 1024 #连接池最大连接数(负值表示没有限制)
max-wait: 10000 #连接池最大阻塞等待时间(负值表示没有限制)
max-idle: 200 #连接池最大空闭连接数
min-idle: 5 #连接汉最小空闲连接数
cloud:
gateway:
routes:
- id: test-consumer-service
uri: "http://localhost:7070/"
predicates:
- Path=/consumer/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 1 # 令牌桶每秒填充速率(代表:每秒生成一个令牌)
redis-rate-limiter.burstCapacity: 2 # 令版桶总容量
redis-rate-limiter.requestedTokens: 1 # 每次请求消费一个令牌
key-resolver: "#{@pathKeyResolver}" # SpElL表达式按名称引用bean
(4). 配置KeyResolver
package help.lixin.conf;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
@Configuration
public class KeyResolverConiguration {
@Bean
public KeyResolver pathKeyResolver() {
// ****************************************************
// URL限流
// 意味着:可以自定义key(可以把请求变成用户ID)
// IP限流
// return (exchange) -> Mono.just(exchange.getRequest().getRemoteAddress().toString());
// ****************************************************
return (exchange) -> Mono.just(exchange.getRequest().getPath().toString());
}
}
(5). 测试
(6). 查看redis信息
127.0.0.1:6379> keys *
1) "request_rate_limiter.{/consumer}.timestamp"
2) "request_rate_limiter.{/consumer}.tokens"
127.0.0.1:6379> get "request_rate_limiter.{/consumer}.timestamp"
"1608110283"
127.0.0.1:6379> get "request_rate_limiter.{/consumer}.tokens"
"0"
(7). 总结
- redis-rate-limiter.replenishRate: 1,为令牌桶每秒填充速率(代表每秒向令牌桶添加一个令牌).
- redis-rate-limiter.burstCapacity: 2,为令版桶总容量.
- 在RequestRateLimiter的内部,每一次执行请求,都记住最后一次的时间.
- 从令牌通拿一个令牌,当拿不到的情况下,代表令牌不足,就触发:5,6,7步.
- 根据当前时间 减 上一次执行时间,计算出相差了多少”秒”,比如:相差10秒.
- 相差的10秒 * redis-rate-limiter.replenishRate = 要产生的令牌个数为:10个.
- 向令牌桶产生10个令牌(由于受:redis-rate-limiter.burstCapacity限制,只能生成2个),这个过程实际就是更改Redis的值(“request_rate_limiter.{/consumer}.tokens”).
- 从令牌通拿一个令牌,当拿不到的情况下,代表令牌不足,就触发:5,6,7步.
- 拿令牌的过程就是对redis进行自减(“request_rate_limiter.{/consumer}.tokens”)
- 为什么用Redis+Lua,就是为了解决Redis操作一致性问题.
- 缺点:在整个Spring Cloud Gateway进程里,KeyResolver实例只允许有一个,否则,就会抛出异常.