我正在编写一个可写的流模块。我想为我的用户实现管道接口。
如果发生某些错误,我需要暂停可读流并发出错误事件。然后,用户将决定 - 如果他没有错误,他应该能够恢复数据处理。
var writeable = new BackPressureStream();
writeable.on('error', function(error){
console.log(error);
writeable.resume();
});
var readable = require('fs').createReadStream('somefile.txt');
readable.pipe.(writeable);
我看到该节点为我们提供了readable.pause()
方法,可用于暂停可读流。但我无法从可写的流模块中调用它:
var Writable = require('stream').Writable;
function BackPressureStream(options) {
Writable.call(this, options);
}
require('util').inherits(BackPressureStream, Writable);
BackPressureStream.prototype._write = function(chunk, encoding, done) {
done();
};
BackPressureStream.prototype.resume = function() {
this.emit('drain');
}
如何在可写流中实现背压?
P.S。可以使用提供可读流作为参数的pipe/unpipe
事件。但也有人说,对于管道流,暂停的唯一机会是从可写入中删除可读流。
我做对了吗?我必须取消管理可写流,直到用户呼叫恢复?在用户调用恢复后,我应该管道可读流回来了吗?
答案 0 :(得分:1)
无需访问源流或与源流交互。原生NodeJS流现在支持背压和缓冲。 pipe()
负责两者。
您只需要正确实施_write()
。
function _write(chunk, enc, callback) {
// if you don't invoke callback, data is buffered, and writes paused when buffer is full
}
引用文档:
在所有时间之间发生的对writable.write()的调用 调用writable._write()并调用回调将导致 要缓冲的书面数据。
转发错误后,在用户确认继续操作之前,不要为下一个chunk调用callback()
。这将导致数据从源到缓冲区。
当writable.write(chunk)时,数据在Writable流中缓冲 方法被重复调用。而内部的总大小 写入缓冲区低于highWaterMark设置的阈值,调用 writable.write()将返回true。一旦内部的大小 缓冲区达到或超过highWaterMark,将返回false。
在可写流的缓冲区已满后,对write()
的调用将返回false。如果源流实现表现良好或本机节点流,它将自动停止write()
更多数据。
答案 1 :(得分:1)
您所描述的内容已经由pipe
方法本身实现。来自文档的Errors While Writing部分:
如果
Readable
发出错误时,Writable
流通过管道传输到Writable
流中,则Readable
流将被取消管道传输。
因此,作为可写流的实现者,您唯一的工作就是实现_write
方法并在发生这种情况时发出错误。流模块将自动处理取消配管。然后,如果模块的使用者将错误视为非关键错误,则他们的工作就是将可读流通过管道传回。他们可以这样做:
var writeable = new BackPressureStream();
var readable = require('fs').createReadStream('somefile.txt');
writeable.on('error', function(error) {
// use pipe again, if error is not critical
if (!error.critical) {
readable.pipe(writeable);
} else {
readable.destroy(error);
}
});
readable.pipe(writeable);
在模块内部:
BackPressureStream.prototype._write = function(chunk, encoding, done) {
// call done with an error to emit 'error' event and unpipe readable stream
done(new Error('BOOM'));
};
答案 2 :(得分:0)
基本上,正如我所理解的那样,在发生错误事件时,您希望在流上加压。你有几个选择。
首先,正如您已经确定的那样,使用pipe
来获取读取流的实例并做一些花哨的步法。
另一种选择是创建一个提供此功能的包装可写流(即它需要WritableStream
作为输入,并且在实现流功能时,将数据传递给提供的流。
基本上你最终会得到像
这样的东西 source stream -> wrapping writable -> writable
https://nodejs.org/api/stream.html#stream_implementing_a_writable_stream涉及实现可写流。
关键是如果底层可写中发生错误,您将在流上设置一个标志,并且下一次调用write
,您将缓冲该块,存储回调和只打电话。像
// ...
constructor(wrappedWritableStream) {
wrappedWritableStream.on('error', this.errorHandler);
this.wrappedWritableStream = wrappedWritableStream;
}
// ...
write(chunk, encoding, callback) {
if (this.hadError) {
// Note: until callback is called, this function won't be called again, so we will have maximum one stored
// chunk.
this.bufferedChunk = [chunk, encoding, callback];
} else {
wrappedWritableStream.write(chunk, encoding, callback);
}
}
// ...
errorHandler(err) {
console.error(err);
this.hadError = err;
this.emit(err);
}
// ...
recoverFromError() {
if (this.bufferedChunk) {
wrappedWritableStream.write(...this.bufferedChunk);
this.bufferedChunk = undefined;
}
this.hadError = false;
}
注意:您应该只需要实现write
函数,但我鼓励您深入挖掘并使用其他实现函数。
值得注意的是,写入发生错误事件的流可能会遇到一些问题,但我会将此作为一个单独的问题解决。
这是另一个关于背压的好资源 https://nodejs.org/en/docs/guides/backpressuring-in-streams/