之前做过一个 Node.js实现分片上传 的性能。过后前端采纳文件切片后并发上传,大大提高了上传的速度,应用Promise.race()治理并发池,无效防止浏览器内存耗尽。

当初的问题:Node.js服务端合并大文件分片 内存耗尽导致服务重启

服务端代码

const ws = fs.createWriteStream(`${target}/${filename}`); // 创立将要写入文件分片的流const bufferList = fs.readdirSync(`${STATIC_TEMPORARY}/${filename}`); // 读取到分片文件名的列表[1,2,3...] 每个分片1M大小// 2. 不会阻塞EventLoopbufferList.forEach((hash, index) => {  fs.readFile(`${STATIC_TEMPORARY}/${filename}/${index}`, (err, data) => {       ws.write(data);  });});

服务器配置:RAM:1024MB 1vCPU (运行了一些其余服务)

测试发现只有上传的文件超过300M,在合并分片时就会导致内存耗尽服务重启,根本无法实现分片合并。

解决方案:能不能在循环中管制读取文件的并发数呢?这样就不会有大量文件同时读取到内存中导致服务解体了。

尝试像前端那样应用 Promise.race 管制:

const ws = fs.createWriteStream(`${target}/${filename}`); // 创立将要写入文件分片的流const bufferList = fs.readdirSync(`${STATIC_TEMPORARY}/${filename}`); // 读取到分片文件名的列表[1,2,3...] 每个分片1M大小// Promise.race 并发管制const pool = [];let finish = 0; // 曾经写入实现的分片数量// 应用Promise包裹读取文件的异步操作const PR = (index) => {  return new Promise((resolve) => {    fs.readFile(`${STATIC_TEMPORARY}/${filename}/${index}`, (err, data) => {      ws.write(data)      resolve({});    });  });};(async function easyRead() {  for (let i = 0; i < bufferList.length; i ++) {    const task = PR(i).then(val => {      finish+=1      const index = pool.findIndex(t => t === task);      pool.splice(index);      if (finish === bufferList.length) {        ws.close();      }    });    pool.push(task);    if (pool.length === 1) { // 这里并发数量只能是1 否则分片写入是乱序的 格局会被损坏      await Promise.race(pool);    }  }})()

这时神奇的事件就产生了,咱们在for循环中应用了Promise.race()管制了同一时间读入到内存中的文件数量。

再次测试,服务曾经不会在合并分片时解体,即便1个G的文件分片也能够在3秒左右合并实现。

总结

晓得Promise.race()的人很多,这样的面试题也很多,然而能使用到实际中解决理论的问题却不是很多。

心愿本文能够帮到你。

文章首发于 IICOOM-技术博客 《应用Promise.race()实现管制并发》