在高并发中,有三把利器来保护系统:缓存、降级和限流。那么何为限流,就是限制流量,对系统的出入流量进行控制,防止大流量出入,导致资源不足,系统不稳定。
常见的三种限流算法:计数器算法,滑动窗口,令牌桶算法
1.计数器算法
比如规定,对于A接口来说,我们一分钟的访问次数不能超过100次。那么我们可以这么做,在一开始的时候,我们可以设置一个计数器,每当一个请求过来的时候,计数器加一,如果计数器值大于100且并且该请求与第一次请求的间隔还在一分钟之内。那么说明请求过多;如果该请求与第一个请求的间隔大于1分钟,且计数器值还在限流器范围内。那么就重置计数器。
public class CounterTest { public long timeStamp = getNowTime(); public int reqCount = 0; public final int limit = 100; // 时间窗口内最大请求数 public final long interval = 1000; // 时间窗口ms public boolean grant() { long now = getNowTime(); if (now < timeStamp + interval) { // 在时间窗口内 reqCount++; // 判断当前时间窗口内是否超过最大请求控制数 return reqCount <= limit; } else { timeStamp = now; // 超时后重置 reqCount = 1; return true; } } public long getNowTime() { return System.currentTimeMillis(); } }
优点:能够实现控制并发数的效果
缺点:使用场景单一,适用单机,一般用来对入流量进行控制
2.滑动窗口
roling windows
上图中,整个红色的矩形表示一个时间窗口,在我们的例子中,一个时间窗口就是一个分钟,然后我们将时间窗口进行划分,比如途中,我们就将滑动窗口化成6格,所以每格代表10秒钟。每过10秒钟,我们的时间窗口就会往右滑动一格。每一个格子都有自己对应独立的计数器,比如当一个请求在0:35秒的时候到达,那么0:30~0:39对应的计数器就会加1
当0:59到达100格请求会落在灰色格子里,而1:00到达的请求会落在橘黄色的格子中。当时间到达1:00时,我们的窗口会往右移动一格,那么此时时间窗口内的总请求数量一共是200个,超过了限定的100个,所以此时能够检测出来触发了限流。
--- 资源唯一标识 local key = KEYS[1] --- 时间窗最大并发数 local max_window_concurrency = tonumber(ARGV[1]) --- 时间窗 local window = tonumber(ARGV[2]) --- 时间窗内当前并发数 local curr_window_concurrency = tonumber(redis.call('get', key) or 0) if current + 1 > limit then return false else redis.call("INCRBY", key,1) if window > -1 then redis.call("expire", key,window) end return true end
优点: 通过时间窗最大请求数可以直接换算出最大的QPS(QPS = 请求数/时间窗)
缺点: 可能出现流量不平滑,时间窗内一小段流量占比特别大。
3.令牌桶
1)所有请求在处理之前都需要拿到一个可用的令牌才会被处理
2)根据限流大小,设置按照一定速率往桶里添加令牌
3)桶设置最大的放置令牌限制,当桶满时,新添加的令牌就被丢弃或者拒绝
4)请求到达后首先要获取令牌桶中的令牌,拿着令牌才可以进行其他的业务逻辑,处理业务逻辑后,将令牌直接删除
5) 令牌桶右最低限额,当桶中的令牌达到最低限额的时候,请求处理完之后将不会删除令牌,以此保证足够的限流
博客来源:
https://www.cnblogs.com/linjiqin/p/9707713.html
https://mp.weixin.qq.com/s/3KF7Rcy5y3_RNFQZe4HX5Q