Node.js 如何将背压应用于节点流?

Node.js 如何将背压应用于节点流?,node.js,stream,Node.js,Stream,在尝试Node.JS流时,我遇到了一个有趣的难题。当输入(可读)流推送更多数据时,目标(可写)关心的是我无法正确应用背压 我尝试的两种方法是从writeable.prototype.\u write返回false,并保留对Readable的引用,以便我可以从writeable.prototype调用Readable.pause()。这两种解决方案都没有多大帮助,我将对此进行解释 在我的练习(您可以查看)中,我有三条流: 可读密码发生器 我认为this.push()的返回代码足以自动暂停并等待dr

在尝试Node.JS流时,我遇到了一个有趣的难题。当输入(可读)流推送更多数据时,目标(可写)关心的是我无法正确应用背压

我尝试的两种方法是从
writeable.prototype.\u write
返回false,并保留对Readable的引用,以便我可以从writeable.prototype调用
Readable.pause()
。这两种解决方案都没有多大帮助,我将对此进行解释

在我的练习(您可以查看)中,我有三条流:

可读密码发生器 我认为
this.push()
的返回代码足以自动暂停并等待
drain
事件恢复

变换哈希器 只需将密码的哈希值添加到对象中

可写样本消费者 理论上,写入流将返回false,导致流缓冲到缓冲区满为止(默认情况下,
objectMode
为16),最终Readable将调用它的
this.pause()
方法。但是
16+16+16=48
;缓冲区中有48个对象,直到东西填满,系统堵塞。实际上更少,因为不涉及克隆,所以它们之间传递的对象是相同的引用。难道这不意味着只有16个对象在记忆中,直到高水位线停止一切吗

最后,我意识到我可以使用可写引用和可读引用来使用闭包调用它的pause方法。然而,这个解决方案意味着可写流知道很多关于另一个对象的信息。我必须提交一份推荐信:

var foo = new PasscodeGenerator('foobar');
foo
  .pipe(new Hasher('md5'))
  .pipe(new SampleConsumer(samples, foo));
这让人觉得流的工作方式不正常。我认为反压力足以导致一个可写程序停止可读程序推送数据,并防止内存不足错误

一个类似的例子是Unix
head
命令。在Node中实现这一点,我将假设目标可以结束,而不仅仅是忽略导致源继续推送数据,即使目标有足够的数据来满足文件的开始部分

我如何习惯性地构造自定义流,以便在目标准备结束时,源流不会尝试推送更多数据?

这是一个关于如何在内部调用
\u read()
的示例。由于您的
\u read()
总是同步/立即推送,因此内部流实现可以在正确的条件下进入循环<代码>\u read()实现是执行某种异步I/O(例如,从磁盘或网络读取)

解决方法(如上面链接中所述)是使您的
\u read()
至少在某些时候是异步的。您也可以在每次使用以下命令调用时使其异步:

PasscodeGenerator.prototype._read = function(n) {
  var passcode = '' + this.prefix + this.count;
  var self = this;

  // `setImmediate()` delays the push until the beginning
  // of the next tick of the event loop
  setImmediate(function() {
    self.push({passcode: passcode});
  });

  this.count++;
};
util.inherits(SampleConsumer, stream.Writable);
function SampleConsumer(max) {
  stream.Writable.call(this, {objectMode: true});
  this.max   = (max != null) ? max : 10;
  this.count = 0;
}
SampleConsumer.prototype._write = function(sample, encoding, next) {
  this.count++;
  console.log('Hash %d (%s): %s', this.count, sample.passcode, sample.hash);
  if (this.count < this.max) {
    next();
  } else {
    return false;
  }
};
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory
Aborted (core dumped)
var foo = new PasscodeGenerator('foobar');
foo
  .pipe(new Hasher('md5'))
  .pipe(new SampleConsumer(samples, foo));
PasscodeGenerator.prototype._read = function(n) {
  var passcode = '' + this.prefix + this.count;
  var self = this;

  // `setImmediate()` delays the push until the beginning
  // of the next tick of the event loop
  setImmediate(function() {
    self.push({passcode: passcode});
  });

  this.count++;
};