常见流控算法

计数器

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

计数器限流存在时间临界点的问题,比如每分钟限速100个请求,第59秒来了100个请求,第61秒又来了100个请求,59秒到61秒瞬间来了200个请求。

public class Counter {
    private static long timeStamp = System.currentTimeMillis();

    // 每秒限制50个请求
    private static long limitCount = 50;
    private static long interval = 1000;
    private static long reqCount = 0;

    public static boolean grant() {
        long now = System.currentTimeMillis();
        if (now < timeStamp + interval) {
            if (reqCount < limitCount) {
                ++reqCount;
                return true;
            } else {
                return false;
            }

        } else {
            timeStamp = System.currentTimeMillis();
            reqCount = 0;
            return false;
        }
    }


    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    if (grant()) {
                        System.out.println("执行中");
                    } else {
                        System.out.println("限流中");
                    }
                }
            }).start();
        }
    }
}

滑动窗口

滑动窗口的意思是说把固定时间片,进行划分,并且随着时间的流逝,进行移动,这样就巧妙的避开了计数器的临界点问题。也就是说这些固定数量的可以移动的格子,将会进行计数判断阀值,因此格子的数量影响着滑动窗口算法的精度。虽然滑动窗口有效避免了时间临界点的问题,但是依然有时间片的概念。

计数器算法其实就是滑动窗口算法的一个特例,只有一个时间窗口。当滑动窗口的格子划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越精确。

滑动窗口由于需要存储多份的计数器,每个窗口一份计数器,所以滑动窗口在实现上需要更多的存储空间。也就是说,如果滑动窗口的精度越高,需要的存储空间就越大。

漏桶算法

滑动窗口虽然避免了临界点问题,但还是依赖时间片,漏桶算法在这方面比滑动窗口而言,更加先进。有一个固定的桶,进水的速率是不确定的,但是出水的速率是恒定的,当水满的时候是会溢出的。漏桶算法不支持任持续突发和最大突发,天生就限制了请求的速度。当使用了漏桶算法,我们可以保证接口会以一个常速速率来处理请求。所以漏桶算法天生不会出现临界问题。

import static java.lang.Long.max;

public class LeakBucket {

        private static long timeStamp = System.currentTimeMillis();
        private static long capacity = 10;  // 桶的容量
        private static long water = 0;      // 当前水量(当前累积请求数)
        private static long rate = 5;       // 水流出速度

        public static boolean grant() {
            long now = System.currentTimeMillis();
            water = max(0, water - (now - timeStamp) * rate); // 先执行漏水,计算剩余水量
            timeStamp = now;
            if ((water + 1) < capacity) {
                water += 1;
                return true;
            }
            else {
                return false;
            }
        }

    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    if (grant()) {
                        System.out.println("执行中");
                    } else {
                        System.out.println("限流中");
                    }
                }
            }).start();
        }
    }
}

令牌桶算法

一个存放令牌的桶,以一定的速度往这个桶生成令牌,数据流出先从这个桶中拿令牌,若是拿不到令牌就另行处理(具体自己设定)。桶令牌跟漏斗最大的区别在于可以支撑一个突然的流量变化,就是满桶令牌数的峰值。

import static java.lang.Long.min;

public class TokenBucket {

    private static long timeStamp = System.currentTimeMillis();
    private static long capacity = 10; // 桶的容量
    private static long rate = 20;     // 令牌放入速度
    private static long tokens = 5;    // 当前令牌数量
    private static boolean grant() {
        long now = System.currentTimeMillis();
        tokens = min(capacity, tokens + (now - timeStamp) * rate);
        timeStamp = now;
        if (tokens < 1) {
            return false;
        }
        else {
            tokens -= 1;
            return true;
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    if (grant()) {
                        System.out.println("执行中");
                    } else {
                        System.out.println("限流中");
                    }
                }
            }).start();
        }
    }
}

默认从桶里移除令牌是不需要耗费时间的,如果给移除令牌设置一个延时,那么令牌桶算法就变成了漏桶算法。 虽然令牌桶算法允许突发速率,但是下一个突发速率必须要等桶内有足够的 token后才能发生

参考

https://blog.csdn.net/jek123456/article/details/77152571

https://www.cnblogs.com/xuwc/p/9123078.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值