Node.js如何逐对象处理大型JSON文件以避免堆内存上限报错
问题根因
- 解析路径配置错误:当前使用的
JSONStream.parse('offers.*')会匹配到offers下的完整offer数组,需等所有数组成员全部解析完成后才会一次性抛出,3GB大小的文件对应的数组完全加载后必然超出堆内存限制。 - 多文件并行读取:for循环会同时启动所有压缩文件的读取解析流,多个大文件同时处理会进一步推高内存占用。
修复方案
- 调整JSONStream匹配路径为
offers.offer.*,每解析完成一个offer数组内的对象就单独抛出,无需等待整个数组加载。 - 改为串行处理文件,处理完成一个文件后再读取下一个,避免同时占用过多内存。
- 优化流处理逻辑,正确响应背压,避免数据在内存中堆积。
修改后代码
const { parentPort } = require('worker_threads') const zlib = require('zlib') const fs = require('fs') const JSONStream = require('JSONStream') const es = require('event-stream') // 封装单个文件的处理逻辑,返回Promise方便串行控制 async function processGzFile(gzFile) { return new Promise((resolve, reject) => { const gunzip = zlib.createGunzip() // 修改匹配路径为每个offer数组元素 const parser = JSONStream.parse('offers.offer.*') const readStream = fs.createReadStream(gzFile) readStream .pipe(gunzip) .pipe(parser) // 现在每次回调拿到的是单个offer对象,不是数组 .pipe(es.map((rawProduct, callback) => { const processedProduct = getProcessedProduct(rawProduct) parentPort.postMessage({ processedProduct }) callback() })) .on('error', (e) => { console.trace(`Error while reading file ${gzFile}`, e) reject(e) }) .on('end', () => { resolve() }) }) } // 串行处理所有文件 async function processAllFiles(zippedFiles) { for (const gzFile of zippedFiles) { await processGzFile(gzFile) } // 所有文件处理完成后发送完成信号 parentPort.postMessage({ completed: true }) } // 启动处理 processAllFiles(zippedFiles)
可选优化
如果服务器内存充足,可在启动Node进程时添加参数扩大堆内存上限,示例如下(数值为MB,可根据实际情况调整):node --max-old-space-size=8192 your-worker-file.js
内容的提问来源于stack exchange,提问作者lightPT
相关产品推荐
相关产品推荐

