限流算法

1.为什么要限流

  • 系统上游流量未知,如果超载引起雪崩。
  • 系统下游吞吐能力一般,直连带宽有限。

2.常见的几种限流算法

  1. 计数器限流

    在一段时间间隔内,对请求进行计数,与阀值进行比较判断是否需要限流,一旦到了时间临界点,将计数器清零

    优点:简单

    缺点:很难处理单位时间内的临界问题

 

 

  demo代码示例:

public class CountLimiter implements Limiter {
 
    //时间窗口
    private long timeStamp= System.currentTimeMillis();
    //请求次数
    private int reqCount;
    //时间间隔
    private long interval=1000;
    //限流次数
    private long limitNum=100;
 
    /**
     * (单机限流,分布式限流用 redis)
     * 返回 true 代表限流, false 代表通过
     * @return
     */
    @Override
    public synchronized Boolean limit(){
        long now = System.currentTimeMillis();
 
        //判断当前时
        if(now<timeStamp+interval){
            //判断档期窗口内请求次数是否超过每秒限流的最大请求次数
            if(reqCount+1>limitNum){
                //限流了
                return true;
            }
            reqCount++;
            return false;
 
        }else {
            //开启新的时间窗口
            timeStamp=now;
            //重置计数器
            reqCount=1;
            return false;
 
        }
    }
}

 

  2. 滑动窗口

    把固定时间片进行划分,并且随着时间的流逝,进行移动,固定数量的可以移动的格子,进行计数并判断阀值

    优点:将固定时间段分块,时间比“计数器”复杂,适用于稍微精准的场景

    缺点:格子的数量影响着滑动窗口算法的精度,依然有时间片的概念,无法根本解决临界点问题

 

 

   demo代码示例

  

package com.example.limit;
 
import java.util.LinkedList;
 
public class SlideTimeWindowLimiter implements Limiter {
 
    private int reqCount;
    //记录滑动的窗口的10个格子
    private LinkedList<Integer> slots = new LinkedList<>();
    //每秒限流的最大请求次数
    private int limitNum = 100;
    //滑动时间窗口里的每个格子的时间长度,单位 ms
    private long windowLength = 100L;
    //滑动时间窗口里的格子数量
    private int windowNum = 10;
 
 
    @Override
    public synchronized Boolean limit() {
        if ((reqCount + 1) > limitNum) {
            return true;
        }
        slots.set(slots.size() - 1, slots.peekLast() + 1);
        reqCount++;
        return false;
    }
 
 
    //滑动窗口实现
    public SlideTimeWindowLimiter(){
        slots.addLast(0);
        new Thread(()->{
            while (true){
                try {
                    Thread.sleep(windowLength);
                }catch (InterruptedException e){
                    e.printStackTrace();
                }
                synchronized (this){
                    slots.addLast(0);
                    if(slots.size()>windowNum){
                        //滑动窗口,删除第一个格子,
                        reqCount=reqCount-slots.peekFirst();
                        slots.removeFirst();
                    }
                }
 
            }
        }).start();
    }
 
 
}

  3. 漏桶算法

     一个固定容量的漏桶,按照固定速率流出水滴

     特点:

        1.以任意速率往桶中放入水滴。

        2.以固定速率从桶中流出水滴。

        3.如果流入水滴超出了桶的容量,则流入的水滴溢出(新请求被拒绝)

    优点:可以很好的控制消费频率

    缺点:限制的是常量流出速率(即流出速率是一个固定常量值),所以最大的速率就是出水的速率,不能出现突发流量

 

 

demo代码示例

package com.example.limit;
 
public class LeakyBucketLimiter implements Limiter {
 
    private long timeStamp=System.currentTimeMillis();
    private long capacity=100;//桶的容量
    private long rate=10//水漏出的速度(每秒系统能处理的请求数)
    private long water=20//当前水量(当前累计的请求数)
 
    @Override
    public synchronized Boolean limit() {
        long now = System.currentTimeMillis();
        water = Math.max(0, water - ((now - timeStamp) / 1000) * rate);
        timeStamp = now;
        if ((water + 1) <= capacity) {
            //加水
            water++;
            return false;
        else {
            //水满拒绝加水;
            return true;
        }
    }
}

4. 令牌桶算法

     一个固定的桶,桶里存放着令牌(token)。一开始桶是空的,系统按固定的时间(rate)往桶里添加令牌,直到桶里的令牌数满,多余的请求会被丢弃。当请求来的时候,从桶里移除一个令牌,如果桶是空的则拒绝请求或者阻塞。

     特点:

        1. 流入桶的速率一定

        2.请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务

        3.能够应对突发流量

 

 

demo代码示例

package com.example.limit;
 
public class TokenBucketLimiter implements Limiter {
    private long timeStamp=System.currentTimeMillis();
    private long capacity=100;//桶的容量
    private long rate=10// 令牌放入速度
    private long tokens=20// 当前令牌数量
 
    @Override
    public Boolean limit() {
        long now = System.currentTimeMillis();
        //添加令牌
        tokens = Math.min(capacity, tokens + (now - timeStamp) / 1000 * rate);
        timeStamp = now;
        if (tokens < 1) {
            return true;
        else {
            //还有令牌,领取令牌
            tokens--;
            return false;
        }
    }
}
 
 
写评论...
posted @ 2022-12-13 08:30  纵码万水千山  阅读(59)  评论(0编辑  收藏  举报