Node.js - 使用 createReadStream 和 createWriteStream 时内存使用率高

Node.js - High memory usage when using createReadStream and createWriteStream

我正在用节点测试流,我设置了一个程序来读取一个大文件并使用流再次写入它。问题是当 运行 程序时,节点的内存使用量上升到 1.3 GB,这正是正在读取的文件的大小。就像它不流式传输它,它缓冲它并一次性写入它或者垃圾收集器不会破坏内存中的 chunk 变量。这是程序:

const {  createReadStream, createWriteStream } = require('fs');

const readStream = createReadStream('../movie.mp4', {
    highWaterMark: 10000
});
const writeStream = createWriteStream('./copy.mp4', {
    highWaterMark: 10000
});

readStream.on('data', function (chunk) {
    writeStream.write(chunk);
})

readStream.on('end', function () {
    console.log("reading done");
    writeStream.end();
});

writeStream.on('close', function () {
    console.log("Writing done.");
})

奇怪的是,如果我通过管道传输这些流,它会按预期工作并且内存使用量不会超过 20 MB。像这样:

const {  createReadStream, createWriteStream } = require('fs');

const readStream = createReadStream('../movie.mp4', {
    highWaterMark: 10000
});
const writeStream = createWriteStream('./copy.mp4', {
    highWaterMark: 10000
});

readStream.pipe(writeStream);

什么会导致这种行为?

节点版本:v14.15.4

嗯,我找到问题了。这种情况称为背压。在我的例子中,它发生是因为读取流比写入流快得多。这是因为 readStream 在内存中缓冲读取数据,直到 writeMemory 写入它。所以解决方案是我们暂时暂停 readStream 直到 writeStream 完成写入,然后我们为它提供更多数据块。 这是正确的程序:

const {  createReadStream, createWriteStream } = require('fs');

const readStream = createReadStream('../movie.mp4', {
    highWaterMark: 10000
});

const writeStream = createWriteStream('./copy.mp4', {
    highWaterMark: 10000
});


readStream.on('data', function (chunk) {
    // according to docs the value of result variable is: 
    // Returns: <boolean> false if the stream wishes for the calling code to wait for the 'drain' event to be emitted before continuing to write additional data; otherwise true.
    const result = writeStream.write(chunk);

    if(!result) {
        console.log("BACKPRESSURE");
        readStream.pause();
    }
});

writeStream.on('drain', () => {
    console.log("DREAINED");
    readStream.resume();
});

readStream.on('end', function () {
    console.log("reading done");
    writeStream.end();
});

writeStream.on('close', function () {
    console.log("Writing done.");
})

关于 drain 事件的文档是 here