限流算法

计数器

我们知道,限流算法最简单的就是计数器法,当时间间隔超过设定的k秒后,就拒绝请求,简单的伪代码就是:

//k为设定的时间间隔

if(now < (start_time + k))
{
    // k 秒内小于阈值
    if(capacity < ioslevel)
        capacity++;
    else
        reject ;
}
else{
    start_time = now;
    capacity = 0;
}

但是这样的话,就会导致:假设时间间隔为1秒,那么在1秒末突然请求到达阈值,而下一秒初的请求也达到阈值,那么在这段时间内,请求数是超过1秒设定的阈值。

滑动窗口

从而改进算法就是,基于滑动窗口,保证任意请求的1秒间隔内的容量不超过设定的阈值

看下图

从而,我们可以基于循环队列这种数据结构来实现,刚开始,实现的时候,被绕的有点晕,感觉对循环队列每太明白,并且对于与限流的滑动窗口没有结合好,浪费了很多时间

思路:

//tail指向最新时间
//head指向最旧时间

//超过时间间隔
if(time_interval >= k)
    //将最旧时间向前移
    //将最新时间存储到tail中
    tail = (tail + 1) % size;
    queue[tail] = now;
    head = (head + 1) % size;
else        //未超过时间间隔
    //若为超过容量,存储最新时间
    if((tail + 1) % size != head)
        tail = (tail + 1) % size;
        queue[tail] = now;
    else
        reject;

下面是实现code,注意我这里为了保证移动正确,在head、tail初始化时,表示第一个容量已经被用了,当第一个请求过来的时候将放在1的位置,而不是最初0的位置(这么做是为了我的算法实现正确)

//容量设为了10

class SKI_Window{
public:
	// 初始化
	SKI_Window(int second) :time_interval(0),head(0),tail(0),k(second) {
		que[0] = time(0);
	};

	void skiWindow()
	{
		time_t now = time(0);
		tm * now_s = gmtime(&now);
		int sec = now_s->tm_sec;
		int min = now_s->tm_min;
		//cout << ctime(&now) << endl;
		//cout << ctime(&que[head]) << endl;
		
		tm *s_stamp = gmtime(&que[head]);
		int s_sec = s_stamp->tm_sec;
		int s_min = s_stamp->tm_min;
		// 秒的间隔,只计算了最多分钟的差别,没有计算小时的
		time_interval = (min-s_min)*60+ sec - s_sec;
		int check = 0;
		if (time_interval >= k)
		{
			tail = (tail + 1) % 10;
			que[tail] = now;
			head = (head + 1) % 10;
			cout << "timeout timeout timeout timeout timeout!!!!!!!!!!!!" << endl;
		}
		else
		{
			// 没超过队列就一直向前走
			if ((tail + 1) % 10 != head)
			{
				tail = (tail + 1) % 10;
				que[tail] = now;
			}
			else
			{
				cout << "request invalid" << endl;
			}
		}

		//cout << "interval: " << time_interval << endl;
		//cout << "#####################" << endl;

	}
private:
	int time_interval;
	time_t que[10];
	int head ;
	int tail ;
	int k;

};

漏桶算法

思路:计算两次请求之间应该倒出多少水,从而获得一些容量,最大不超过漏斗容量;

当请求过来时,与可使用容量进行比较,当有可使用容量时,就允许请求,此时可使用容量减1,当不可使用时(可使用容量为0),拒绝请求。

class FunnelRateLimiter {
private:
	// 容量
	int capacity;
	// 每秒漏水的速度
	double leakingRate;
	// 漏斗没有被占满的体积
	int emptyCapacity;
	// 上次漏水的时间
	time_t lastLeakingTime ;
	// 时间间隔
	int time_interval;

public:
	FunnelRateLimiter(int c, int speed) :capacity(c), leakingRate(speed),lastLeakingTime(time(0)),time_interval(0) {
		emptyCapacity = c;
	};

	FunnelRateLimiter(int capacity, double leakingRate) {}

	void makeSpace() {
		time_t now = time(0);
		tm * now_s = gmtime(&now);
		int sec = now_s->tm_sec;
		int min = now_s->tm_min;

		tm *s_stamp = gmtime(&lastLeakingTime);
		int s_sec = s_stamp->tm_sec;
		int s_min = s_stamp->tm_min;
		// 秒的间隔,没有按微秒计算产生令牌数
		time_interval = (min - s_min) * 60 + sec - s_sec;
		// 计算离上次漏斗的时间到现在漏掉的水
		int deltaQuota = time_interval * leakingRate;
		// 更新上次漏的水
		lastLeakingTime = now;
		// 更新腾出的空间
		emptyCapacity += deltaQuota;
		// 超出最大限制 复原
		if (emptyCapacity > capacity) {
			emptyCapacity = capacity;
		}

	}

	boolean isActionAllowed() {
		makeSpace();
		// 如果有可使用容量
		if (emptyCapacity > 0) {
			// 给腾出空间注入流量
			emptyCapacity--;
			return true;
		}
		return false;
	}
};

 

 

令桶牌

漏斗算法会以一个稳定的速度转发,而令牌桶算法平时流量不大时在“攒钱”,流量大时,可以一次发出队列里有的请求,而后就受到令牌桶的流控限制。

另外,令牌桶还可能做成第三方的一个服务,这样可以在分布式的系统中对全局进行流控。

  • 有一定容量的一个桶,按照一定速率来产生令牌
  • 两次请求的时间间隔下产生的令牌超过容量,那么最多只产生该容量大小的令牌
  • 每次请求都消耗掉一个令牌
  • 当桶中没有令牌就拒绝请求

class Buckets
{
public:
	Buckets(int c,int n):capacitys(c),timestamp(time(0)),time_interval(0),nums(n) {
        timestamp = time(0);
    };


	void compute_tokens()
	{
		time_t now = time(0);
		tm * now_s = gmtime(&now);
		int sec = now_s->tm_sec;
		int min = now_s->tm_min;
		
		tm *s_stamp = gmtime(&timestamp);
		int s_sec = s_stamp->tm_sec;
		int s_min = s_stamp->tm_min;
		// 秒的间隔,没有按微秒计算产生令牌数
		time_interval = (min - s_min) * 60 + sec - s_sec;
		//最多能产生多少个令牌
		int token = min(capacitys, tokens+time_interval * nums);
		timestamp = now;
		//还有令牌
		if (token-->0)
		{
			cout << "allow request!!!" << endl;
			tokens = token;
		}
		else
		{
			tokens = 0;
			cout << "reject request!!!" << endl;
		}
	}
private:
	int capacitys;
	time_t timestamp;
	int tokens;
	int time_interval;
};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值