在高并发的情况下,我们可以把消息放入队列,在从队列消费,达到限流的目的。但这里说的限流指的是当我们请求其他服务器接口,防止高并发下把对面服务器压垮,于是对我们要求每秒限制在100QPS。
如果使用springCloud可以用hystrix限流,如果使用springCloud-alibaba可以使用sentinal实现限流,那么如果不依赖组件,如何实现接口限流呢?
限流的策略有:拒绝服务,等待排队,服务降级。
最简单拒绝服务可以直接返回异常抛出,请用户稍后再试。
但对一些重要的接口,比如下单,秒杀等,我们不希望用户请求太快,也不希望拒绝失败,这种可以放到队列。
那么服务降级则是触发限制条件,直接返回兜底数据,比如查询库存默认返回有库存。
- 计数器限流(固定窗口限流)
我们可以通过AtomicLong来限制发送的数量,比如一分钟之内发送60条数据,那么平均每秒发送1条数据,当超过一分钟的时候,再把计数器归零,如果没有超过一分钟,则大于60的时候,不允许继续访问接口。
但这样有一个致命缺点,如果有恶意用户进行访问,在59s的时候发送60条,在1min01s的时候发送60条,那么在2s之内就处理了120条数据,这时候很有可能把服务器压垮而且这种情况下,剩余的59s都属于空闲状态,也是不合理的。。
说到底,这是颗粒度太大,不够精细导致的,那么如何解决呢?
- 滑动窗口
先定义一个key,在通过redis管道实现滑动窗口判断是否符合限流行数。
管道里放入的是这个滑动窗口的数据量,每次新增都会删除超过时间的元素,而且限流最大请求次数是灵活的。
- 漏桶算法
算法实现是,我们有一个固定容量的桶,有水流进来,也有水流出去,我们可以控制水流出的速率和,对于进来的水,我们无法控制进来多少和速率,当桶满的时候,则进入拒绝策略。
我们只要把算法里的水换成用户请求。
这个算法也有缺陷,因为当拒绝的时候,会丢弃,而且在高并发的情况下,处理效率不高,于是有了下面广泛使用的令牌漏桶算法。
- 令牌漏桶算法
系统会维护一个令牌桶,以恒定的速度往里面放令牌,这时候如果有请求,则先看要有没有令牌,没有则限制请求。