今天我们来学习怎么使用 Spring Cloud Gateway 来实现限流。
话不多说,开始今天的学习。
限流配置
1. 相关概念
限流是对某一时间窗口内的请求数进行限制,保持系统的可用性和稳定性,防止因流量暴增而导致的系统运行缓慢或宕机。
常见的限流算法有: 计数器
算法, 漏桶(Leaky Bucket)
算法, 令牌桶(Token Bucket)
算法。
Spring Cloud Gateway
官方提供了 RequestRateLimiterGatewayFilterFactory
过滤器工厂,使用 Redis
和 Lua
脚本实现了 令牌桶 的方式。
令牌桶算法 是对漏桶算法的一种改进,漏桶算法能够限制请求调用的速率,而令牌桶算法能够在限制调用的平均速率的同时还允许一定程度的突发调用。在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝。
放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃令牌。所以就存在这种情况,桶中一直有大量的可用令牌,这时进来的请求就可以直接拿到令牌执行,比如设置qps为100,那么限流器初始化完成一秒后,桶中就已经有100个令牌了,这时服务还没完全启动好,等启动完成对外提供服务时,该限流器可以抵挡瞬时的100个请求。所以,只有桶中没有令牌时,请求才会进行等待,最后相当于以一定的速率执行。
网上找了一个图:
2. 代码实战
上面已经提到过, Spring Cloud Gateway
官方提供了 RequestRateLimiterGatewayFilterFactory
过滤器工厂,使用 Redis
和 Lua
脚本实现了 令牌桶 的方式。
所以我们还需要引入 redis 的 reactive 依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
application.yml
配置文件
server:
port: 8080
spring:
application:
name: api-gateway
redis:
host: localhost
port: 6379
password:
cloud:
gateway:
routes:
- id: cloud-gateway
uri: http://192.168.1.211:8088/
predicates:
- Path=/ytb/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 1 # 令牌桶每秒填充速率
redis-rate-limiter.burstCapacity: 2 # 令牌桶总容量
key-resolver: "#{@pathKeyResolver}" # 使用 SpEL 表达式按名称引用 bean
在上面的配置文件,配置了 redis
的信息,并配置了 RequestRateLimiter
的限流过滤器,该过滤器需要配置三个参数:
-
burstCapacity
,令牌桶总容量。 -
replenishRate
,令牌桶每秒填充平均速率。 -
key-resolver
,用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据 #{@beanName} 从 Spring 容器中获取 Bean 对象。
编写 URI
限流规则配置类
@Configuration
public class KeyResolverConfiguration {
@Bean
public KeyResolver pathKeyResolver(){
return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
}
}
然后本地启动一个 redis,重启项目,我们就可以测试看看效果了
还是原来的接口,在我们快速刷新了几次之后会发现返回 HTTP ERROR 429
说明我们的限流起了作用了,同时限流成功看到 redis
中维护了两个键
request_rate_limiter.{file/getFileType}.timestamp
request_rate_limiter.{xxx}.tokens
其他限流规则
参数限流: key-resolver: "#{@parameterKeyResolver}"
(以下配置要求请求路径中必须携带 userId 参数)
@Bean
public KeyResolver parameterKeyResolver()
{
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
}
IP限流: key-resolver: "#{@ipKeyResolver}"
@Bean
public KeyResolver ipKeyResolver()
{
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}
过滤器规则(Filter)
过滤规则 | 实例 | 说明 |
---|---|---|
PrefixPath | - PrefixPath=/app | 在请求路径前加上app |
RewritePath | - RewritePath=/test, /app/test | 访问localhost:9022/test,请求会转发到localhost:8001/app/test |
SetPath | SetPath=/app/{path} | 通过模板设置路径,转发的规则会在路径前增加app,{path}表示原请求路径 |
RedirectTo | 重定向 | |
RemoveRequestHeader | 去掉某个请求头信息 |
StripRefix
StripPrefix
就表明截取路径的个数
spring:
application:
name: api-gateway
cloud:
gateway:
routes:
- id: cloud-gateway
uri: http://192.168.1.211:8088
predicates:
- Path=/ytb/my/**
filters:
- StripPrefix=2
这里就表示访问地址 http://localhost:8080/ytb/my/file/getFileType
会被转发到 http://192.168.1.211:8088/file/getFileType
地址。
作者:程序员秃头之路
链接:微服务系列:服务网关 Spring Cloud Gateway 限流配置 - 掘金