若页面一次性接口请求上百个,阁下又当如何应对?

4,241 阅读2分钟

前言

问:假如页面一次性请求有上百个,你应该如何处理这种请求并发?

答:so easy!循环请求?肯定是不对的,否则一次性并发上百次请求,差点的服务器得崩溃了,我甚至一度以为你是在搞Dos攻击。我们可以通过任务队列的缓存来合理控制并发数据。

我们知道浏览器发起的请求最大并发数量一般都是 6~8 个,这是因为浏览器会限制同一域名下的并发请求数量,以避免对服务器造成过大的压力。本文思路的核心就是保持浏览器的最大并发请求,多出的请求数加入队列缓存。

1. 实现并发请求场景

我们现实一下简单的100次直接并发请求的场景,本地实现一个简单的前端页面和express后台服务:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="./axios.min.js"></script>
</head>
<body>  
</body>

<script>
  // 请求模拟
  const requestFn = () => {
    return axios.get("http://127.0.0.1:8000/msg", {})
  }
  // 模拟直接并发100次请求
  for (let i = 0; i < 100; i++) {
    requestFn();
  }
</script>
</html>
const express = require('express');
const app = express();

app.all('*', (req,res,next) => {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Credentials", true);
  next();
})

app.get("/msg", (req, res) => {
  let count = 0;
  for (let i = 0; i < 10000000; i++) {
    count++
  }
  res.json({code :0, msg: `请求结果${count}`});
})

app.listen('8000',() => {
  console.log('请求成功')
})

一次性并发上百个请求,要是配置低一点,又或者带宽不够的服务器,直接宕机都有可能,所以我们前端这边是需要控制的并发数量去为服务器排忧解难。

2. 优化并发请求场景

如下所示,定义一个 queue 用来缓存请求队列,定义一个参数 current 记录当前正常并发执行的接口请求数量。每一次循环请求时都会先将请求接口缓存在队列queue中,当前的并发的请求数量 current 小于默认最大的请求限制数量 limitCount时候,从队列头部中选择一个请求并开始执行,并且 current 数量+1,执行完成后current 数量-1,并且继续递归执行。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <script src="./axios.min.js"></script>
</head>
<body>  
</body>

<script>
  // 请求模拟
  const requestFn = () => {
    return axios.get("http://127.0.0.1:8000/msg", {})
  }
  // 实现一个并发控制的函数
  const queue = [] // 请求池队列,用来缓存接口请求
  let current = 0
  const requestQueue = (limitCount = 6, callbackFn) => {
    queue.push(callbackFn); // 入队列
    const dequeue = () => {
      while (current < limitCount && queue.length) {
        current++;
        const requestPromiseFactory = queue.shift() // 出列
        requestPromiseFactory()
          .then((res) => { 
            console.log('res =====>', res)
          })
          .catch(error => { // 失败
            console.log(error)
          })
          .finally(() => {
            current--
            dequeue()
          });
      }
    }
    // 执行
    dequeue()
  }

  for (let i = 0; i < 100; i++) {
    requestQueue(6, requestFn)
  }
</script>
</html>