通过stdin在node.js中处理大量数据



旧题目:

">node.js readline form net。Socket (process.stdin)导致错误:堆内存不足(转换net. stdin)。套接字双工到可读流)">

…我改了,因为没有人回答,这似乎是一个重要的问题在node.js生态系统。

问题是如何解决"堆内存不足"的问题从巨大的stdin逐行读取时出错?当您将标准输出转储到文件(例如:test.log)并通过fs.createReadStream('test.log')读取到'readline'接口时,没有发生错误。

看起来像进程。stdin不是这里提到的可读流:https://nodejs.org/api/process.html process_process_stdin

为了重现这个问题,我创建了两个脚本。首先是生成大量的数据(a.js文件):

// a.js
// loop in this form generates about 7.5G of data
// you can check yourself running:
// node a.js > test.log && ls -lah test.log
// will return
// -rw-r--r--  1 sd  staff   7.5G 31 Jan 22:29 test.log
for (let i = 0 ; i < 8000000 ; i += 1 ) {
console.log(`${i} ${".".repeat(1000)}n`);
}

通过bash管道使用readline (b.js文件)的脚本:

const fs = require('fs');
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin, // doesn't work
//input: fs.createReadStream('test.log'), // works
});
let s;
rl.on('line', line => {
// deliberaty commented out to demonstrate that issue
// has nothing to do beyond readline and process.stdin
// s = line.substring(0, 7);
//
// if (s === '100 ...' || s === '400 ...' || s === '7500000') {
//
//   process.stdout.write(`${line}n`);
// }
});
rl.on('error', e => {
console.log('general error', e)
})

现在当你运行;

node a.js | node b.js

将导致错误:

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

但是如果你交换

const rl = readline.createInterface({
input: process.stdin,
});

const rl = readline.createInterface({
input: fs.createReadStream('test.log')
});

和运行

node a.js > test.log
node b.js

一切正常

问题实际上归结为如何转换网络。套接字到功能齐全的可读流?,如果可能的话。

<标题>

编辑:基本上我的问题是,它似乎是不可能处理大量的数据从stdin作为一个流,这是自然的Unix风格的管道。因此,尽管node.js在处理流方面非常出色,但你无法编写通过unix风格的管道处理大量数据的程序。

由于这个限制,在某些情况下完全没有必要将数据转储到硬盘驱动器,只有在使用fs.createReadStream('test.log')处理之后才可以。

我认为流都是关于在飞行中处理大量数据(以及其他用例)而不将其保存在硬盘上。

你总是可以把process.stdin当作一个正常的NodeJS流,自己处理读取:

const os = require('os');
function onReadLine(line) {
// do stuff with line
console.info(line);
}
// read input and split into lines
let BUFF = '';
process.stdin.on('data', (buff) => {
const content = buff.toString('utf-8');
for (let i = 0; i < content.length; i++){
if (content[i] === os.EOL) {
onReadLine(BUFF);
BUFF = '';
} else {
BUFF += content[i];
}
}
});
// flush last line
process.stdin.on('end', () => {
if (BUFF.length > 0) {
onReadLine(BUFF);
}
});

的例子:

// unix
cat ./somefile.txt | node ./script.js
// windows
Start-Process -FilePath "node" -ArgumentList @(".script.js")  -RedirectStandardInput .somefile.txt -NoNewWindow -Wait

问题不是输入数据大小,不是Node,而是数据生成器的错误设计:它没有在消费者输出流的请求下实现暂停/恢复数据生成。而不是仅仅将数据推入console.log(..),你应该正确地与标准输出流交互,并正确地处理来自该流的pauseresume信号。

fs.createReadStream()创建的文件输入流是正确实现的,它会根据需要暂停/恢复,因此不会使代码崩溃。

最新更新