关于接口的请求的一些思考


前言

在优化小程序发布平台的时候,当时研究代码的时候,发现同事写的一段代码,是用来做异步任务切割的,当时很好奇这个任务切割可以解决什么问题。

调研

由于微信那边的限制,一次性只能同时发起 6 个接口请求,如果发起的更多就会报错。所以才有了这个任务切割的代码,但是在我实际的测试过程中发现,同时发起的 6 个请求,其实还是串行执行的,上一个结束之后才会调用下一个,然后我就对比了各个方式的调用结果。

测试

一次性发送 100 个请求

const post = () => {
  return new Promise((resolve, reject) => {
    fetch("http://localhost:18740/users/1")
      .then((res) => res.json())
      .then((r) => {
        console.log(r);
        resolve(r);
      });
  });
};

for (let index = 0; index < 100; index++) {
  post();
}

await 串行调用 100 个接口

const post = () => {
  return new Promise((resolve, reject) => {
    fetch("http://localhost:18740/users/1")
      .then((res) => res.json())
      .then((r) => {
        console.log(r);
        resolve(r);
      });
  });
};

for (let index = 0; index < 100; index++) {
  await post();
}

分组调用,等待上一组接口全部返回再调用下一组

const flatJobs = (jobs, concurrent_count) => {
  const concurrentCount = concurrent_count || 6;
  return jobs.reduce(
    (queues, c, i) => {
      if (i % concurrentCount > 0) {
        queues[queues.length - 1].push(c);
        return queues;
      }

      queues.push([c]);
      return queues;
    },
    [[]]
  );
};

const runSerialJobsQueue = async (jobs) => {
  let p = 0;
  const res = [];
  while (p < jobs.length) {
    const part_res = await Promise.all(
      jobs[p].map((fn) => fn().catch(() => false))
    );
    res.push(...part_res);
    ++p;
  }
  return res;
};

const post = () => {
  return new Promise((resolve, reject) => {
    fetch("http://localhost:18740/users/1")
      .then((res) => res.json())
      .then((r) => resolve(r));
  });
};

const rows = Array(100).fill(0);

const jobs = rows.map((row) => {
  return async () => {
    const res = await post(row);
    return { res };
  };
});

const j = flatJobs(jobs);
runSerialJobsQueue(j);

连接程池调用,只要有连接调用结束释放连接,便开始新的连接

async function asyncPool(poolLimit, array, iteratorFn) {
  // if (shouldAssert) {
  //   assertType(poolLimit, "poolLimit", ["number"]);
  //   assertType(array, "array", ["array"]);
  //   assertType(iteratorFn, "iteratorFn", ["function"]);
  // }
  const ret = [];
  const executing = [];
  for (const item of array) {
    const p = Promise.resolve().then(() => iteratorFn(item, array));
    ret.push(p);

    if (poolLimit <= array.length) {
      const e = p.then(() => executing.splice(executing.indexOf(e), 1));
      executing.push(e);
      if (executing.length >= poolLimit) {
        await Promise.race(executing);
      }
    }
  }
  return Promise.all(ret);
}

const post = () => {
  return new Promise((resolve, reject) => {
    fetch("http://localhost:18740/users/1")
      .then((res) => res.json())
      .then((r) => {
        console.log(r);
        resolve(r);
      });
  });
};

const results = asyncPool(6, Array(100).fill(0), post);

思考

可以看出,无论是先循环中一次性跑完异步请求,还是 await 串行去跑接口,或者说分组用 promise.all 去执行,或者用线程池去执行,100 个接口的用时都几乎没有差别。那就有了新的疑问,既然还是一个个处理的,为什么超过 6 个,微信就给限制了,反正也是一个个处理的。

经过分析,其实这 100 个接口虽然是同时发起,也就是并发的,但是却不是同时执行(并行)的,微信既限制了并发量,也限制了并行量,所以才导致了这样的结果。

当然,微信去限制并发和并行是为了安全考虑的,一次性发送太多接口,会导致系统炸了!


文章作者:   1874
文章链接:   https://1874.cool/epmi4g/
版权声明:   本博客所有文章除特別声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 1874 !
评论
 上一篇
跨域场景汇总 跨域场景汇总
常见跨域问题排查及问题解决
下一篇 
代码的马斯洛金字塔 代码的马斯洛金字塔
原文链接马斯洛金字塔是美国心理学家马斯洛提出的一个心理学模型,认为人类的心理需求从下往上分为 5 个层次,一旦实现了下层的需求,就会追求上一层的需求。这五个层次依次是:生理需求、安全需求、社交需求、尊严需求、自我实现。代码质量也可以用金字塔
2021-05-16
  目录