在做文件下载功能时,为了避免下载功能将服务器的带宽打满,从而影响服务器的其他服务。我们可以设计一个限流器来限制下载的速率,从而限制下载服务所占用的带宽。
一、算法思路
定义一个数据块chunk(单位 bytes)以及允许的最大速率 maxRate(单位 KB/s)。通过maxRate我们可以算出,在maxRate的速率下,通过一个数据块大小的字节流所需要的时间 timeCostPerChunk。
之后,在读取/写入字节时,我们维护已经读取/写入的字节量 bytesWillBeSentOrReceive。
当bytesWillBeSentOrReceive达到一个数据块的大小时,检查期间消耗的时间(nowNanoTime-lastPieceSentOrReceiveTick)
如果期间消耗的时间小于timeCostPerChunk的值,说明当前的速率已经超过了 maxRate的速率,这时候就需要休眠一会来限制流量
如果速率没超过或者休眠完后,将 bytesWillBeSentOrReceive=bytesWillBeSentOrReceive-chunkSize
之后在读取/写入数据时继续检查。
下面该算法的Java代码实现:
public synchronized void limitNextBytes(int len) {
//累计bytesWillBeSentOrReceive
this.bytesWillBeSentOrReceive += len;
//如果积累的bytesWillBeSentOrReceive达到一个chunk的大小,就进入语句块操作
while (this.bytesWillBeSentOrReceive > CHUNK_LENGTH) {
long nowTick = System.nanoTime();
//计算积累数据期间消耗的时间
long passTime = nowTick - this.lastPieceSentOrReceiveTick;
//timeCostPerChunk表示单个块最多需要多少纳秒
//如果missedTime大于0,说明此时流量进出的速率已经超过maxRate了,需要休眠来限制流量
long missedTime = this.timeCostPerChunk - passTime;
if (missedTime > 0) {
try {
Thread.sleep(missedTime / 1000000, (int) (missedTime % 1000000));
} catch (InterruptedException e) {
LOGGER.error(e.getMessage(), e);