从事件处理程序写入WriteStream

时间:2014-10-03 21:38:48

标签: javascript node.js event-handling

我有一个EventEmitter对象,我设置为侦听事件。发出事件时,我想将信息写入文件。我通过FileStream开放fs.createWriteStream(path, { flags: 'a'});目前,我的问题是,如果我超级快速地发出事件,我会开始得到备份"。 IE .write返回false让我停止写作片刻。由于我在事件处理程序中执行写操作,因此附近没有用于指示写入过程结束的回调函数。我可以从处理或发射方面做些什么来阻止备份?

最终,它似乎并不重要;所有数据都会写入文件。但是我想遵守"规则"尽我所能。

我知道我可以监听drain事件并在此之后再次开始写,但是如何防止其他事件进入处理程序?我注意到如果我在每次发射之前放置50ms延迟,备份似乎不会发生,但这看起来有点像黑客。如果你有一个较慢的硬盘驱动器怎么办?

以下是我的情况示例:

var ee = new EventEmitter();
var stream = fs.createWriteStream('./file/log.txt', { flags:'a'} );

ee.on('report', function (i) {
  stream.write('new file data ' + i + ' --- '  + Date.now + '\n');
});

for (var i = 0; i < 10000; ++i) {
  ee.emit('report', i)
}

这不是确切的代码,但这是它的要点。完整代码发生在从正在运行的HTTP服务器发送响应时,但如果我排队了1000个请求,例如通过for循环,我就会遇到上述情况。

2 个答案:

答案 0 :(得分:1)

我实际上最终使用读写流找到了一个更简单的解决方案。请参阅以下代码以获取示例

var stream = require('stream');
var fs = require('fs');
var EventEmitter = require('events').EventEmitter;

var ee = new EventEmitter();
var writeStream = fs.createWriteStream('./file/log.txt', { flags: 'a', end: false } );
var readStream = new stream.Readable();
// This needs to be here for compatibility reasons, but is intentionally a no-op
readStream._read = function() {};

ee.on('report', function (i) {
  readStream.push(i.toString());
});

readStream.pipe(writeStream);

for (var i = 0; i < 10000; ++i) {
  ee.emit('report', i);
}

这将允许节点管道和流系统与OS协调处理背压。这是IMO解决这个问题的首选方法。

答案 1 :(得分:0)

处理此问题的理想方法是pause()传入的事件,如果事件来自流或者以其他方式暂停,您可以执行此操作,但这并不总是可行的。

如果你不能以某种方式暂停传入的事件,那么我通常使用queue模块的async函数来处理这个问题。当然还有很多其他方法可以做到这一点,但是使用队列是我发现的最简单的方法,而async模块(对很多异步操作很有用)提供了一个很好的方法。

基本思路是将所有write个调用放入一个配置为一次只处理1个任务的队列。如果您从false来电回复stream.write,那么pause() queue。从drain收到stream个事件后,您再次resume()这个队列。这样,当stream已经饱和时,你就不会写信给stream,但是当var async = require('async'); var ee = new EventEmitter(); var stream = fs.createWriteStream('./file/log.txt', { flags:'a'} ); // Create a queue with a concurrency of 1 var writeQueue = async.queue(function(data, callback) { if (!stream.write(data)) { // if write() returns false, it's saturated; pause the queue writeQueue.pause(); } callback(); }, 1); // <-- concurrency argument here; it's easy to miss ;) stream.on('drain', function() { // the stream isn't saturated anymore; resume the queue writeQueue.resume(); }) ee.on('report', function (i) { // instead of writing directly to the stream, push data to the writeQueue writeQueue.push('new file data ' + i + ' --- ' + Date.now() + '\n'); }); for (var i = 0; i < 10000; ++i) { ee.emit('report', i) } 准备就绪时,您仍然可以接收事件并排队等候它们。

使用示例代码执行此操作将如下所示:

{{1}}

注意:这与让流内部缓冲内容完全不同。您仍然在缓冲数据,您只是自己动手操作,这样可以更好地控制情况。