Node.js:如何优化编写许多文件?

ACPR

我正在Windows的Node环境中工作。我的代码Buffer每秒接收30个对象(每个对象约500-900kb),我需要尽快将这些数据保存到文件系统中,而无需进行任何阻止接收以下对象的工作Buffer(即目标是将每个缓冲区的数据保存大约30-45分钟)。就其价值而言,数据是来自Kinect传感器的连续深度帧。

我的问题是:在Node中写文件最有效的方法是什么?

这是伪代码:

let num = 0

async function writeFile(filename, data) {
  fs.writeFileSync(filename, data)
}

// This fires 30 times/sec and runs for 30-45 min
dataSender.on('gotData', function(data){

  let filename = 'file-' + num++

  // Do anything with data here to optimize write?
  writeFile(filename, data)
}

fs.writeFileSync似乎比快得多fs.writeFile,这就是为什么我在上面使用它。但是,还有其他方法可以对数据进行操作或写入文件来加快每次保存的速度吗?

jfriend00

首先,您永远不要使用它fs.writefileSync()来处理实时请求,因为这会阻塞整个node.js事件循环,直到完成文件写入为止。

好的,基于将每个数据块写入一个不同的文件,那么您想允许同时进行多个磁盘写入,但不能无限进行磁盘写入。因此,使用队列仍然是适当的,但是这次队列不仅一次仅进行一次写入,而且同时具有一定数量的写入:

const EventEmitter = require('events');

class Queue extends EventEmitter {
    constructor(basePath, baseIndex, concurrent = 5) {
        this.q = [];
        this.paused = false;
        this.inFlightCntr = 0;
        this.fileCntr = baseIndex;
        this.maxConcurrent = concurrent;
    }

    // add item to the queue and write (if not already writing)
    add(data) {
        this.q.push(data);
        write();
    }

    // write next block from the queue (if not already writing)
    write() {
        while (!paused && this.q.length && this.inFlightCntr < this.maxConcurrent) {
            this.inFlightCntr++;
            let buf = this.q.shift();
            try {
                fs.writeFile(basePath + this.fileCntr++, buf, err => {
                    this.inFlightCntr--;
                    if (err) {
                        this.err(err);
                    } else {
                        // write more data
                        this.write();
                    }
                });
            } catch(e) {
                this.err(e);
            }
        }
    }

    err(e) {
        this.pause();
        this.emit('error', e)
    }

    pause() {
        this.paused = true;
    }

    resume() {
        this.paused = false;
        this.write();
    }
}

let q = new Queue("file-", 0, 5);

// This fires 30 times/sec and runs for 30-45 min
dataSender.on('gotData', function(data){
    q.add(data);
}

q.on('error', function(e) {
    // go some sort of write error here
    console.log(e);
});

注意事项:

  1. 实验concurrent您传递给Queue构造函数值。从5值开始。然后查看将该值进一步提高是否会带来更好或更差的性能。node.js文件I / O子系统使用线程池来实现异步磁盘写入,因此可以允许最大数量的并发写入,因此将并发数量提高得很高可能不会使事情进展得更快。

  2. UV_THREADPOOL_SIZE在启动node.js应用程序之前,可以通过设置环境变量来增加磁盘I / O线程池的大小

  3. 您最大的朋友是磁盘写入速度因此,请确保您有一块带有良好磁盘控制器的快速磁盘。快速总线上的快速SSD是最好的。

  4. 如果您可以将写操作散布到多个实际物理磁盘上,则可能还会增加写吞吐量(工作的磁盘头更多)。


这是基于问题的初始解释的先前答案(在对其进行更改之前进行编辑)。

由于似乎需要按顺序对磁盘进行写入(全部写入同一文件),因此建议您使用写入流并让流对象序列化并为您缓存数据,或者您可以创建队列你自己像这样:

const EventEmitter = require('events');

class Queue extends EventEmitter {
    // takes an already opened file handle
    constructor(fileHandle) {
        this.f = fileHandle;
        this.q = [];
        this.nowWriting = false;
        this.paused = false;
    }

    // add item to the queue and write (if not already writing)
    add(data) {
        this.q.push(data);
        write();
    }

    // write next block from the queue (if not already writing)
    write() {
        if (!nowWriting && !paused && this.q.length) {
            this.nowWriting = true;
            let buf = this.q.shift();
            fs.write(this.f, buf, (err, bytesWritten) => {
                this.nowWriting = false;
                if (err) {
                    this.pause();
                    this.emit('error', err);
                } else {
                    // write next block
                    this.write();
                }
            });
        }
    }

    pause() {
        this.paused = true;
    }

    resume() {
        this.paused = false;
        this.write();
    }
}

// pass an already opened file handle
let q = new Queue(fileHandle);

// This fires 30 times/sec and runs for 30-45 min
dataSender.on('gotData', function(data){
    q.add(data);
}

q.on('error', function(err) {
    // got disk write error here
});

您可以使用writeStream代替此自定义Queue类,但是这样做的问题是writeStream可能会填满,然后您必须有一个单独的缓冲区作为放置数据的地方。像上面那样使用您自己的自定义队列,可以同时解决这两个问题。

其他可伸缩性/性能注释

  1. 因为您似乎是将数据串行写入同一文件,所以磁盘写入将无法从群集或并行运行多个操作中受益,因为它们基本上必须进行序列化。

  2. 如果您的node.js服务器除了要做这些写操作外还有其他事情要做,那么创建第二个node.js进程并在该其他进程中进行所有磁盘写入可能会有一点优势(必须通过测试进行验证) 。您的主要node.js进程将接收数据,然后将其传递给子进程,该子进程将维护队列并进行写入。

  3. 您可以尝试的另一件事是合并写入。当队列中有多个项目时,可以将它们组合成一个写入。如果写操作已经足够大,那么这可能不会有太大的区别,但是如果写操作很小,则可能会有很大的不同(将许多小磁盘写操作合并为一个大写操作通常会更有效率)。

  4. 您最大的朋友是磁盘写入速度因此,请确保您有一块带有良好磁盘控制器的快速磁盘。快速的SSD是最好的。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章