流量控制算法学习笔记

488 阅读4分钟

简介

流量控制也被称作过载保护,在高并发的系统中,过载保护是非常有必要的,就像是一个电路中的保险丝,用来保证整个电路不会因为电压的突然增加而崩溃掉。那么我们在系统中是怎么实现流量控制的呢?

流量控制算法

其实我在自己的项目中也有在做流量控制这一个方面,我的做法是:

            Integer count = RedisService.get(ak, key, Integer.class);
            if(count  == null) {
                RedisService.set(ak, key, 1);
            }else if(count < maxCount) {
                RedisService.incr(ak, key);
            }else {
                render(response,new Result(1293,"哥哥别刷了",false));
                return false;
            }
        }
        return true;

大概思路就是当用户访问的时候,把用户Token和访问的URL作为KEY,去Redis中查找有没有value,没有的话就设置value为1,否则判断value的值是否大于阈值,如果大于阈值,直接拒绝访问,否则value+1。当然这个键值对是有过期时间的,比如10秒,这样做可以限制某个用户在短时间内(10秒)频繁的请求服务造成服务器过载。

不足:

这种做法的初衷是10s内只接受用户的1w个请求,但是如果在过期时间(10s)的前1s(第9s)的时候用户请求1w次,在第11s(第二个周期)的时候该用户又请求1w次,这样做的话等于在2s内用户请求了2w次服务,显然这个算法处理请求不够平滑,不能够很好地满足限流需求。

漏桶算法

漏桶算法思路很简单,请求先进入到漏桶里,漏桶以固定的速度出水,也就是处理请求,当水加的过快,则会直接溢出,也就是拒绝请求,可以看出漏桶算法能强行限制数据的传输速率。

漏桶算法

long timeStamp = getNowTime(); 
int capacity = 10000;// 桶的容量
int rate = 1;//水漏出的速度 
int water = 100;//当前水量  

public static bool control() {   
    //先执行漏水,因为rate是固定的,所以可以认为“时间间隔*rate”即为漏出的水量
    long  now = getNowTime();
    water = Math.max(0, water - (now - timeStamp) * rate);
    timeStamp = now;

    if (water < capacity) { // 水还未满,加水
        water ++; 
        return true; 
    } else { 
        return false;//水满,拒绝加水
   } 
} 

这个算法能够非常平滑地去处理请求,但是在某些情况下,漏桶算法不能够有效地使用网络资源,因为漏桶的漏出速率是固定的,所以即使网络中没有发生拥塞,漏桶算法也不能使某一个单独的数据流达到端口速率。因此,漏桶算法对于存在突发特性的流量来说缺乏效率。

令牌桶算法

令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。

令牌桶算法

算法描述:

  • 假如用户配置的平均发送速率为r,则每隔1/r秒一个令牌被加入到桶中(每秒会有r个令牌放入桶中);

  • 假设桶中最多可以存放b个令牌。如果令牌到达时令牌桶已经满了,那么这个令牌会被丢弃;

  • 当一个n个字节的数据包到达时,就从令牌桶中删除n个令牌(不同大小的数据包,消耗的令牌数量不一样),并且数据包被发送到网络;

  • 如果令牌桶中少于n个令牌,那么不会删除令牌,并且认为这个数据包在流量限制之外(n个字节,需要n个令牌。该数据包将被缓存或丢弃); 算法允许最长b个字节的突发,但从长期运行结果看,数据包的速率被限制成常量r。对于在流量限制外的数据包可以以不同的方式处理:

    1)它们可以被丢弃;

    2)它们可以排放在队列中以便当令牌桶中累积了足够多的令牌时再传输;

    3)它们可以继续发送,但需要做特殊标记,网络过载的时候将这些特殊标记的包丢弃。

long timeStamp=getNowTime(); 
int capacity; // 桶的容量 
int rate ;//令牌放入速度
 int tokens;//当前令牌数量

bool control() {
   //先执行添加令牌的操作
   long  now = getNowTime();
   tokens = max(capacity, tokens+ (now - timeStamp)*rate); 
   timeStamp = now;   //令牌已用完,拒绝访问

   if(tokens<1){
     return false;
   }else{//还有令牌,领取令牌
     tokens--;
     retun true;
   }
 } 

令牌桶算法能够在限制数据的平均传输速率的同时还允许某种程度的突发传输。

参考

www.jianshu.com/p/36bca4ed6…