之前做过一个 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()实现管制并发》