Gateway网关限流
一、常见限流场景缓存、降级、限流被称为高并发、分布式系统中经常提及的话题。这里讨论网关限流。1.1 限流对象a):请求频率限流(Request rate limiting):限制某个接口的一分钟内的请求数b):并发量限流(Concurrent requests limiting):限制某个服务的处理请求数c):传输速率限流(Transmission rate limiting):限制下载文件速率d
目录
一、常见限流场景
缓存、降级、限流被称为高并发、分布式系统中经常提及的话题。这里讨论网关限流。
1.1 限流对象
a):请求频率限流(Request rate limiting):限制某个接口的一分钟内的请求数
b):并发量限流(Concurrent requests limiting):限制某个服务的处理请求数
c):传输速率限流(Transmission rate limiting):限制下载文件速率
d):限制黑名单用户访问
e):限制某个IP请求
1.2 限流的处理
a):拒绝服务:请求直接抛出异常,如:gateway返回429状态码
b):排队等待:请求放入队列中,等待处理
c):服务降级:请求返回兜底数据等
二、限流算法
2.1 计数器算法
如上图所示,假定1min内限制请求数量为100,一个请求则计数器counter+1。当counter大于100且还在当前的1min内,触发限流;1min过后,counter重新计数。计数器算法存在临界问题,如下图所示:假设有一个恶意用户,他在 0:59 时,瞬间发送了 100 个请求,并且 1:00 又瞬间发送了 100 个请求,那么其实这个用户在 1 秒里面,瞬间发送了 200 个请求。这个临界点上,可以压垮服务。
2.2 漏桶算法(Leaky Bucket)
桶的上面是个水龙头,请求从水龙头流到桶中,水龙头流出的水速不定,有时快有时慢,这种忽快忽慢的流量叫做 Bursty flow。如果桶中水满,多余的水就会溢出,即请求被丢弃。从桶底部漏出的水速是固定的,可以看出漏桶算法可以平滑请求的速率。对于漏桶算法,桶的容量不变,流出的速率不变,其底层结构是个队列(如下图所示)。当请求到来时,不是直接处理,而是放入队列中,另一个线程以固定的速率从队列读取请求,从而达到限流的目的。
基于漏桶算法的特点,该算法主要用途是保护服务,无法处理突发流量,而流出速率缓慢,可能造成服务资源的浪费。
2.3 令牌桶算法(Token Bucket)
令牌桶算法是应用最广泛的限流算法,主要有两部分组成:生产令牌、消费令牌。
- 生产令牌:固定容量的令牌桶,按固定的速率(N/s)往桶中放入令牌,桶满时不再放入;
- 消费令牌:每个请求需要从桶中拿取令牌,当消费速率低于生产速率时,直至桶中令牌满而触发限流,此时请求可以放入缓冲队列或直接拒绝。
令牌桶算法有一个很关键的问题,就是桶容量的设置,这个参数可以让令牌桶算法具备处理突发流量的能力。假如将桶容量设置为 100,生成令牌的速度为每秒 10 个,那么在系统空闲一段时间之后(桶中令牌一直没有消费,慢慢的会被装满),突然来了 50 个请求,这时系统可以直接按每秒 50 个的速度处理,随着桶中的令牌很快用完,处理速度又会慢慢降下来,和生成令牌速度趋于一致。这是令牌桶算法和漏桶算法最大的区别,漏桶算法无论来了多少请求,只会按固定速度进行处理。
2.3 总结三种算法特点
类别 | 特点 | 缺点 |
计数器算法 | 1.结构简单-计数器; 2.临界问题; | 出现临界问题 |
漏斗算法 | 1.固定速率处理请求; 2.保护服务; | 无法处理突发流量 |
令牌桶算法 | 1.固定速率生产令牌; 2.设置容量大小; 3.处理突发流量; | 容量设置不合理,可能压垮服务 |
三、Gateway网关限流
3.1 添加依赖jar包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
3.2 添加配置
spring:
redis:
host: 127.0.0.1
port: 6379
cloud:
gateway:
# gateway发现该eureka下的所有服务
discovery:
locator:
enabled: false
lowerCaseServiceId: true
default-filters:
- name: Hystrix
args:
name: default
fallbackUri: 'forward:/fallback/global'
routes:
- id: ${application.name.instance-test}
predicates:
- Path=/${application.name.instance-test}/**
uri: lb://${application.name.instance-test}
filters:
- name: RequestRateLimiter #官方给的名称,无需改动
args:
redis-rate-limiter.replenishRate: 50 #令牌桶填充速率,每秒50个
redis-rate-limiter.burstCapacity: 100 #令牌桶容量,100个
key-resolver: "#{@apiKeyResolver}" #SpEL表达式从Spring容器中获取Bean对象
3.3 定义KeyResolver的bean对象
配置中key-resolver: "#{@apiKeyResolver}",其中apiKeyResolver对应的是下面方法的名称
// 定义KeyResolver,限制api接口
@Bean
public KeyResolver apiKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getPath().value());
}
3.4 测试
a. redis是否有key
b. 限制请求后,网关直接返回429状态码
3.5 限流的源码
a.RedisRateLimiter的类图
配置过滤器中:- name: RequestRateLimiter,指的是下图类图的实现类,可以自定义限流策略需要继承AbstractRateLimiter,核心代码是org.springframework.cloud.gateway.filter.ratelimit.RedisRateLimiter#isAllowed方法。
b. 实现限流算法的Lua脚本
request_rate_limiter.lua脚本,位置如下图所示。
四、参考资料
https://www.imooc.com/article/290828
https://blog.csdn.net/weixin_36384501/article/details/113073344
https://blog.csdn.net/weixin_34019929/article/details/88023005
更多推荐
所有评论(0)