接口限流是保证系统稳定性的三大法宝之一(缓存, 限流, 降级).
本文使用三种方式实现Api限流, 并提供了一个用Spring实现的Api限流的简单Demo, Demo的git地址: https://github.com/xiaobaoqiu/api-blocking, 其中接口限流配置在文件 blocking-config.properties 中, 内容实例如下:
# 每一块审核一个限流配置,一块内的起始数字相同,数字依次往下递增 # 每一块由一下四个信息组成: # name - 请求url # redirectUrl - 请求被阻塞的时候跳转的 url # duration,limit - 在 duration 秒的时间内最多访问 limit 次 # 表示接口 /business/detail.json 10 秒只能被访问 2 次, 超过的请求讲被跳转到 /noAuth 上 0.url=/business/detail.json 0.redirectUrl=/noAuth 0.duration=10 0.limit=2
里面包含了三种方式来实现限流, 下面将主要审核分别详细介绍三种方式:
1.Redis 2.滑动窗口 3.Guava的RateLimiter
Redis的官网的命令手册的例子就是如何使用 incr 指令实现接口限流.参见官网: https://redis.io/commands/incr/
简单说就是每个请求生成一个key(可以根据IP + 接口url生成, 也可以直接根据接口url生成), value为计数值. 设置过期时间.
需要注意 Redis 的过期策略是混合的:
1.被动删除:当读/写一个已经过期的key时,会触发惰性删除策略,直接删除掉这个过期key; 2.主动删除:Redis会定期(默认好像是100ms)主动淘汰一批已过期的key;当已用内存超过限定时, 也会触发主动清理策略;
大家都知道TCP中的滑动窗口有调节发送速率的作用.这里是一个类似的想法.
按照我们的配置, 我们期望 duration 时间内最多 limit 个请求, 我们可以想象有一个事件窗口, 其宽度就是 duration, 因为每个请求都有一个时间戳(可以用Long表示), 每次请求过来的时候, 我们只需要校验当前请求为尾端的时间窗口内的请求数目是否满足 limit 需求就行了.
实现很简单, 使用一个环形队列就行.具体参考 demo 代码.
直接使用Guava提供的RateLimiter实现.
RateLimiter的原理参考: http://xiaobaoqiu.github.io/blog/2015/07/02/ratelimiter/