简介
本章介绍令牌桶Token Bucket算法在流量限速场景的原理,以及C++实现和相关测试验证。
常见的限流算法有计数限流,固定窗口限流,滑动窗口限流,漏桶算法限流和令牌桶算法限流。令牌桶算法是限流算法的一种,其原理是系统会以一个恒定的速度往桶里放入固定数量的令牌,而如果请求需要被处理,则需要先从桶里获取对应令牌,当桶里没有令牌可取时,则拒绝服务。
令牌桶算法可应用于多种场景,本章是针对网络流控制限制场景的使用,对外发的网络数据进行控制,使数据以长期的平均速率外发,并运行一个瞬时的最高流量。
原理
定义:令牌桶提供了一种机制,限制流的平均速率,并允许流中达到所需的突发级别。
需求:项目的需求是文件下载的服务器端限流,下载请求过来后(请求其他模块有限制,这里忽略这个问题),在独立线程读取文件并发送数据到该请求的socket上,要求发送到网络的总数据要限流,但是数据不能丢弃,可以延迟等待发送。
实现:
- 定时定量向令牌桶投入令牌,令牌桶有容量限制,令牌桶容量满了则丢弃不再投入令牌。
- 在发送报文时,如果令牌桶的令牌数量小于要发送的网络数据大小,则等待令牌桶的数量足够再发送;如果令牌桶的令牌数量大于等于要发送的网络数据大小,则报文直接发送,并减少令牌桶中对应数量的令牌。
结论:
-
限速速率不一定是匀速的,但长期速率是一定的。
在令牌桶满的情况下,如果有突发流量过来,会瞬时消耗掉令牌桶的令牌,此时的理论上限速率为:令牌桶的容量+速率。比如令牌桶的容量为60M,限速速度为50MB/s,那此时的速率最高可到110MB/s的速度。
-
单次发送的数据不能大于令牌容量,否则获取不到令牌。
-
令牌的获取线程安全,可多线程获取。
实现
类图如下:
具体代码如下:
-
CountSemaphore是信号量的封装。
信号量的容量即令牌桶的容量,提供获取令牌和投递令牌2个操作。该对象封装可作为信号量的公共库使用。
代码分析如下:
- acquire获取令牌的操作中,使用锁保护数据正确性,使用条件等待令牌足够才继续往下执行。
- release增加令牌数量,并通知其他等待条件的线程继续执行。
- 因std::mutex是非公平锁,所以获取到锁的线程是随机的,但长期来看是公平的。
具体代码如下:
-
countsemaphore.h
#ifndef COUNTSEMAPHORE_H #define COUNTSEMAPHORE_H #include <mutex> #include <condition_variable> #include <climits> class CountSemaphore {