限流的目的是通过对并发访问/请求进行限速或者一个时间窗口内的请求进行限速来保护系统,将流量削峰防止系统挂掉或雪崩,最终做到有损服务而不是不服务。
4.1 限流算法
4.1.1 令牌桶算法
4.1.2 漏桶算法
4.1.3 计数器限流 比如用Redis的有序集合限流
4.2 应用级限流
4.2.1 限流总并发/连接/请求数
当快要超过系统QPS时,进行限流保护,新请求将被丢弃或者放到队列中
4.2.2 限流总资源数
比如数据库连接和线程,使用池化技术限制总资源数
4.2.3 限流某个接口的总并发数/请求数
单独限制某个高频接口,使用AtomicLong或者信号量,或者Hystrix的信号量模式
4.2.4 限流某个接口时间窗请求数
Guava的Cache统计一定时间窗内的请求次数;Redis的有序集合,score是时间,每次查询时,将时间窗口之外的节点删除掉,查询一下总节点数
4.2.5 平滑限流某个接口的请求数
令牌桶和漏桶,以恒定或者相对恒定速率处理请求
4.3 分布式限流
分布式限流的关键是将限流做成原子化,是4.2的分布式场景下的延伸。针对的场景是业务层面的限流。
4.3.1 Redis+Lua
时间窗口实现:
键值对设计:比如限制每秒并发量,key取时间戳到秒级别,value存请求累加数。
判断的流程:取key,不存在新设置,存在则判断是否大于上限,没超过则将累加数加一。过期时间设置1秒多一点。整个判断写在lua脚本中。
4.3.2 Nginx+Lua
4.4 接入层限流
流量入口处的限流,该层主要目的是:负载均衡、非法请求过滤、请求聚合、缓存、降级、限流、A/B测试、服务质量监控。
相对于分布式限流,这里的限制不涉及业务层面。
对于Nginx接入层限流可以使用自带的两个模块,通过指定key,针对key进行限流:
连接数限流模块:ngx_http_limit_conn_module。比如限定某IP下的总连接数
请求限流模块:ngx_http_limit_req_module。漏桶算法实现,有平滑模式和允许突发模式,比如限定某IP下的总请求数
OpenRestry提供的Lua限流模块:lua-retry-limit-traffic
4.5 节流
防止多个相同事件连续重复执行。
4.5.1 throttleFirst/throttleLast 在一个时间窗口内,多个重复事件只处理第一个或最后一个。
书中举的例子,用户滚动页面触发scroll事件,防止浏览器卡顿,多个滚动时间只会执行一次
4.5.2 throttleWithTimeout 两个连续时间的先后执行时间不得小于某个时间窗口
书中举的例子,关键词自动补全,我们想要一个词的补全而不是每个字的补全,如果每个字都调用补全接口,先输入字的补全会很快被下一个字的补全覆盖