高并发
缓存
、降级
、限流
是保护高并发系统的三把利器。
- 缓存:缓存的目的是提升系统访问速度和增大系统处理容量
- 降级:降级是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行
- 限流:限流的目的是通过对并发访问/请求进行限速,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级等处理.
本文章主要讲解限流
限流
限流:是对某一时间窗口内的请求数进行限制,保持系统的可用性和稳定性,防止因流量暴增而导致的系统运行缓慢或宕机
常用算法
令牌桶
、漏桶(漏斗算法)
和计数器算法
1、 计数器算法
计数器算法是三种算法种最简单的一种算法,原理就是对一段固定时间窗口内的请求进行计数,如果请求数超过了阈值,则舍弃该请求;如果没有达到设定的阈值,则接受该请求,且计数加1。当时间窗口结束时,重置计数器为0。
例如假设对于A接口,1分钟内的访问次数为200次以下。那做出一下设计:开始设置一个计数器counter,每次进行请求时, counter就加1,如果counter的值大于200并且
最后一次请求与第一次请求的间隔时间还在1分钟之内
,说明请求过量, 反之如果最后一次请求与第一个请求的间隔时间大于1分钟,且counter的值小于200,那么就重置 counter为0。
因为为这种算法实现过于简单、在算法限定时间的临界值会产生一些问题、所以常与
滑动窗口算法
一起使用、滑动窗口算法,和TCP网络协议中的算法相似。
public class Counting_sliding_window{
private int windowSize; //窗口大小,毫秒为单位
private int currentLimit; //窗口内限流大小
private int splitNumber; //切分小窗口的数目大小
private int[] counters; //每个小窗口的计数数组
private int index; //当前小窗口计数器的索引
private long startTime; //窗口开始时间
private Counting_sliding_window(){}
public Counting_sliding_window(int windowSize, int currentLimit, int splitNumber){
this.currentLimit= currentLimit;
this.windowSize = windowSize;
this.splitNumber= splitNumber;
counters = new int[splitNumber];
index = 0;
startTime = System.currentTimeMillis();
}
//返回true,则请求通过,否则限流
public synchronized boolean tryAcquire(){
long curTime = System.currentTimeMillis();
long windowsNumber = Math.max(curTime - windowSize - startTime,0) / (windowSize / splitNumber);//计算滑动小窗口的数量
slideWindow(windowsNumber );//滑动窗口
int count = 0;
for(int i = 0;i < splitNum;i ++){
count += counters[i];
}
if(count >= currentLimit){
return false;
}else{
counters[index] ++;
return true;
}
}
private synchronized void slideWindow(long windowsNumber ){
if(windowsNumber == 0)
return;
long splitNumber= Math.min(windowsNumber ,splitNumber);
for(int i = 0;i < splitNumber;i ++){
index = (index + 1) % splitNumber;
counters[index] = 0;
}
startTime = startTime + windowsNumber * (windowSize / splitNumber);//更新滑动窗口时间
}
2、漏斗算法
漏斗算法
:漏斗算法的原理也很容易理解。请求来了之后会首先进到漏斗里,然后漏斗以恒定的速率
将请求流出进行处理,从而起到平滑流量的作用。当请求的流量过大时,漏斗达到最大容量时会溢出,此时请求被丢弃
- 一个固定容量的漏桶,按照常量固定速率流出水滴;
- 如果桶是空的,则不需流出水滴;
- 可以以任意速率流入水滴到漏桶;
- 如果流入水滴超出了桶的容量,则流入的水滴溢出了(被丢弃),而漏桶容量是不变的
3、令牌桶
令牌桶
:令牌桶是一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌,填满了就丢弃令牌,请求是否被处理要看桶中令牌是否足够,当令牌数减为零时则拒绝新的请求。令牌桶允许一定程度突发流量,只要有令牌就可处理,支持一次拿多个令牌。令牌桶中装的是令牌
这里提供使用google的maven的jar包
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>28.2-jre</version>
</dependency>