刻度器

限流算法小结

发布时间:2022/10/11 20:20:27   

计时器限流

计数器是一种比较简单的限流算法,用途比较广泛,在接口层面,很多地方使用这种方式限流。在一段时间内,进行计数,与阈值进行比较,到了时间临界点,将计数器清0。

优点:实现简单,有人心想简单也算优点嘛?大道至简,用简单的方式有效处理复杂问题,本身就是优点。

缺点:存在临界问题,如在12:01:00到12:01:58这段时间内没有用户请求,然后在12:01:59这一瞬时发出个请求,OK,然后在12:02:00这一瞬时又发出了个请求,那这某个1秒区间内,超过了每秒次的请求阈值。

代码实现

publicclassCounterLimiter{privatestaticlongtimeStamp=System.currentTimeMillis();//1秒内允许请求数privatestaticlonglimitCount=;//请求间隔数privatestaticlonginterval=0;//请求限流数privatestaticAtomicLongreqCount=newAtomicLong(0);publicstaticbooleangrant(){longnow=System.currentTimeMillis();if(nowtimeStamp+interval){if(reqCount.get()limitCount){reqCount.incrementAndGet();returntrue;}returnfalse;}else{timeStamp=System.currentTimeMillis();reqCount.set(0);returnfalse;}}}

滑动窗口限流

将时间划分为多个区间,在每个区间内每有一次请求就将计数器加一维持一个时间窗口,占据多个区间,每经过一个区间的时间,则抛弃最老的一个区间,并纳入最新的一个区间,如果当前窗口内区间的请求总和超过了限制数量,则本窗口内所有的请求都被丢弃。

优点:滑动窗口计数器是通过将窗口再细分,并且按照时间“滑动”,这种算法避免了固定窗口计数器带来的双倍突发请求。缺点:时间区间的精度越高,算法所需的空间容量就越大。

代码实现

publicclassSlidingWindowLimiter{/***循环队列,就是装多个窗口用,该数量是windowSize的2倍*/privateAtomicInteger[]timeSlices;/***队列的总长度*/privateinttimeSliceSize;/***每个时间片的时长,以毫秒为单位*/privateinttimeMillisPerSlice;/***共有多少个时间片(即窗口长度)*/privateintwindowSize;/***在一个完整窗口期内允许通过的最大阈值*/privateintthreshold;/***该滑窗的起始创建时间,也就是第一个数据*/privatelongbeginTimestamp;/***最后一个数据的时间戳*/privatelonglastAddTimestamp;publicSlidingWindowLimiter(inttimeMillisPerSlice,intwindowSize,intthreshold){this.timeMillisPerSlice=timeMillisPerSlice;this.windowSize=windowSize;this.threshold=threshold;this.timeSliceSize=windowSize*2;reset();}/***初始化队列*/privatevoidreset(){beginTimestamp=System.currentTimeMillis();if(timeSlices!=null){return;}AtomicInteger[]localTimeSlices=newAtomicInteger[timeSliceSize];for(inti=0;itimeSliceSize;i++){localTimeSlices[i]=newAtomicInteger(0);}timeSlices=localTimeSlices;}/***添加元素,模拟操作*

paramcount*

return*/publicbooleanaddCount(intcount){intindex=locationIndex();System.out.println(index:+index);clearFromIndex(index);intsum=0;//在当前时间片里继续+1sum+=timeSlices[index].addAndGet(count);//加上前面几个时间片里的数量for(inti=1;iwindowSize;i++){sum+=timeSlices[(index-i+timeSliceSize)%timeSliceSize].get();}System.out.println(sum+---+threshold);lastAddTimestamp=System.currentTimeMillis();returnsum=threshold;}/***计算当前所在的时间片的位置*

return*/privateintlocationIndex(){longnow=System.currentTimeMillis();if((now-lastAddTimestamp)timeMillisPerSlice*windowSize){reset();}return(int)(((now-beginTimestamp)/timeMillisPerSlice)%timeSliceSize);}/***然后清空自己前面windowSize到2*windowSize之间的数据格的数据*譬如1秒分4个窗口,那么数组共计8个窗口*当前index为5时,就清空6、7、8、1。然后把2、3、4、5的加起来就是该窗口内的总和*

paramindex*/privatevoidclearFromIndex(intindex){for(inti=1;i=windowSize;i++){intj=index+i;if(j=windowSize*2){j-=windowSize*2;}timeSlices[j].set(0);}}}

漏桶限流

将每个请求视作“水滴”放入“漏桶”进行存储,“漏桶”以固定速率向外“漏”出请求来执行如果“漏桶”空了则停止“漏水”,如果“漏桶”满了则多余的“水滴”会被直接丢弃。

优点:漏桶算法多使用队列实现,服务的请求会存到队列中,服务的提供方则按照固定的速率从过往队列中取出请求并执行,过多的请求则放在队列中排队或直接拒绝。

缺点:漏桶算法的缺陷也很明显,当短时间内有大量的突发请求时,即便此时服务器没有任何负载,每个请求也都得在队列中等待一段时间才被响应。

代码实现

publicclassLeakyBucketLimiter{//时间刻度privatestaticlongtimeStamp=System.currentTimeMillis();//桶大小privatestaticintbucketSize=10;//每ms流出的请求privatestaticintrate=1;//当前的水量privatestaticlongcount=0;publicstaticbooleangrant(){longnow=System.currentTimeMillis();//计算出水量longout=(now-timeStamp)*rate;//先执行漏水,计算剩余水量count=Math.max(0,count-out);timeStamp=now;if((count+1)bucketSize){//桶未满,可以执行count++;returntrue;}//水满拒绝执行returnfalse;}}

令牌桶算法

令牌以固定速率生成,生成的令牌放入令牌桶中存放,如果令牌桶满了则多余的令牌会直接丢弃,当请求到达时,会尝试从令牌桶中取令牌,取到了令牌的请求可以执行,如果桶空了,那么尝试取令牌的请求会被直接丢弃。

优点:令牌桶算法既能够将所有的请求平均分布到时间区间内,又能接受服务器能够承受范围内的突发请求,因此是目前使用较为广泛的一种限流算法。

缺点:牺牲小部分流量。

代码实现

publicclassTokenBucketLimiter{publicstaticlongtimeStamp=System.currentTimeMillis();//桶的容量publicstaticintbucketSize=10;//令牌放入速度publicstaticintrate=3;//当前令牌数量publicstaticlongtokens=0;publicstaticbooleangrant(){longnow=System.currentTimeMillis();//计算需要放桶中数量longin=(now-timeStamp)*rate;tokens=Math.min(bucketSize,tokens+in);//判断桶中是否还有令牌if(tokens1){tokens--;returntrue;}returnfalse;}}



转载请注明:http://www.aideyishus.com/lktp/1778.html
------分隔线----------------------------