限流——漏桶算法及Redis-Cell限流模块

漏桶算法

限流——漏桶算法及Redis-Cell限流模块_第1张图片

漏桶算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水,当水流入速度过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率。

public class FunnelRateLimiter {
    static class Funnel{
        int capacity; //漏斗容量
        float leakingRate; //漏嘴流水速率
        int leftQuota; //漏斗剩余空间
        long leakingTs; //上一次漏水时间

        public Funnel(int capacity, float leakingRate) {
            this.capacity = capacity;
            this.leakingRate = leakingRate;
            this.leftQuota = capacity;
            this.leakingTs = System.currentTimeMillis();
        }
        void makeSpace(){
            long nowTs = System.currentTimeMillis();
            long deltaTs = nowTs - leakingTs;
            int deltaQuota = (int)(deltaTs * leakingRate);
            //间隔时间太长,整数数字过大溢出
            if(deltaQuota < 0){
                this.leftQuota = capacity;
                this.leakingTs = nowTs;
                return;
            }
            //腾出空间太小,最小单位是1
            if(deltaQuota < 1){
                return;
            }
            this.leftQuota += deltaQuota;
            this.leakingTs = nowTs;
            if(this.leftQuota > this.capacity){
                this.leftQuota = this.capacity;
            }
        }
        boolean watering(int quota){
            makeSpace();
            if(this.leftQuota >= quota){
                this.leftQuota -= quota;
                return true;
            }else{
                return false;
            }
        }
    }
    private Map funnels = new HashMap<>();
    public  boolean isActionAllowed(String userId,String actionKey,int capacity,float leakingRate,int quota){
        String key = String.format("%s:%s", userId, actionKey);
        Funnel funnel = funnels.get(key);
        if(funnel == null){
            funnel = new Funnel(capacity,leakingRate);
            funnels.put(key,funnel);
        }
        return funnel.watering(quota);
    }
}

例如,限制用户一分钟只允许连续发帖10次,写一个循环来模拟一直向系统发送请求,漏斗容量是10,说明最开始用户可以连续发帖10次,然后再次发帖的时候,就会判断漏斗中是否有剩余空间,如果漏斗已经满了,返回false,设定漏嘴漏水速率为10f/(60*1000),单位是“操作次数/毫秒”,表示每60秒10次。

/**
 * 测试漏斗算法
 * @throws Exception
 */
static void testFunnel() throws Exception{
    //一分钟只允许连续发帖10次
    FunnelRateLimiter funnelRateLimiter = new FunnelRateLimiter();
    for(int i=1;i<=10000;i++){//模拟发请求
        boolean actionAllowed = funnelRateLimiter.isActionAllowed("sixj", "发帖", 10, 10f/(60*1000),1);
        Thread.sleep(1000);
        System.out.println(i+":"+actionAllowed);
    }
}

redis-cell

Redis4.0以后开始支持扩展模块,redis-cell模块实现限流功能,需要安装,提供了原子的限流指令,支持分布式。

> cl.throttle sixj:reply 10 10 60 1

可以与上面漏桶算法做个对比,

第一个参数    sixj:reply     #key

第二个参数    10               #漏斗容量

第三、四个参数   10 60    #每60秒10次操作

第五个参数     1                #每次进入漏斗的量(可选参数,默认为1)

返回结果

1)(integer) 0 #0表示允许,1表示拒绝

2)(integer)10 #漏斗容量

3)(integer)9 #漏斗剩余空间

4)(integer)-1 #如果被拒绝了,需要多长时间再试(漏斗有空间了,单位秒)

5)(integer)6 #多长时间后,漏斗完全空出来

如果被拒绝了,就需要丢弃或重试,直接取出返回结果的第四个值进行sleep即可,如果不想阻塞线程,也可以异步定时任务来重试。

你可能感兴趣的:(限流——漏桶算法及Redis-Cell限流模块)