限流

278 阅读2分钟

1.背景

        系统中某功能页面上有多个点击操作,每个点击操作都是要向服务器端发起请求,并且系统中该功能的使用用户人数*N(>1000),存在高并发问题.

2.实现

        将学生比作前端发起的请求,将教室比作后端服务器,限流就是限制部分学生进入教室,也就是不允许某些请求到后端服务器.

代码中需要三个变量以及一个方法:

  • windowSize      限流的时间
  • limitCount         限制的时间内,最多发起多少个请求
  • resourcePool     限制的时间内,已发请求的时间戳
  • tryAcquire          限制的时间内,判断是否可以发请求

变量值根据业务需求来定.

核心代码:

// 限流的时间 单位(毫秒)
let windowSize = 10 * 1000;

// 允许多少个请求
let limitCount = 10;

// 存储的时间戳 的资源池
let resourcePool = [];

let tryAcquire = function () {   
    let currentTimeMillis = new Date().getTime();    

  // 资源池未满   
 if (resourcePool.length < limitCount) {       
        resourcePool.push(currentTimeMillis);        
        return true; 
    }

    // 资源池 第一个过期了
    // 当前时间戳和资源池中第一个时间戳大于限流的时间,则资源池中第一个时间戳无效
    // 删除它并将当前的时间戳添加到资源池
    if (currentTimeMillis - resourcePool[0] > windowSize) {        
        resourcePool.shift();        
        resourcePool.push(currentTimeMillis);        
        return true;  
      }  
   return false;
}

3.测试

3.1.测试代码

let count = 12;
setInterval(function () {    
    console.log("3s后的操作");    
    for (let i = 0; i < count; i++) {        
        if (!tryAcquire()) {          
            console.log("资源加载过于频繁");           
            return false;        
        }      
        console.log("加载资源", i);   
     }
}, 3000);

3.2.预测结果

         变量值是10s内可发10次请求,测试代码中,定时器3s执行一次;

  1. 定时器第一次是3s(限制时间0s,第一次调用)后执行,for循环12次,前10次的加载资源正常显示,第11次,因为限流,所以显示了资源加载过于频繁,定时器第一次回调结束
  2. 定时器第二次再3s(限制时间3s)后执行,for循环12次,因为限流,只显示了资源加载过于频繁,定时器第二次回调结束
  3. 定时器第三次再3s(限制时间6s)后执行,for循环12次,因为限流,只显示了资源加载过于频繁,定时器第三次回调结束
  4. 定时器第四次再3s(限制时间9s)后执行,for循环12次,因为限流,只显示了资源加载过于频繁,定时器第四次回调结束
  5. 定时器第五次再3s(限制时间0s)后执行,重复定时器第一次回调...即循环步骤1-4

4.测试结果

5.源码地址:github.com/snailsmail/…