流出highWaterMark的误会

Lud*_*udo 13 node.js

在阅读Github上的一些代码后,似乎我误解了这个highWaterMark概念是如何工作的.

在可写流的情况下,尽可能快地写入大量数据,这是我对生命周期的想法:

1)虽然highWaterMark未达到限制,但流能够缓冲和写入数据.

2)如果highWaterMark达到限制,则流不能再缓冲,因此#write方法返回false以告知您尝试写入的内容将不会写入(从不).

3)一旦流发出drain事件,这意味着缓冲区已被清理,您可以从"被拒绝"的位置再次写入.

在我看来这很清楚也很简单,但看起来这并不完全正确(在第2步),当#write方法返回false时,你尝试编写的数据真的被"拒绝"吗?或者是缓冲(或其他)?

对不起基本问题,但我需要确定!

Mat*_*son 17

2)如果达到highWaterMark限制,流不能再缓冲,所以#write方法返回false,让你知道你试写的东西不会写(从不).

这是错误的,数据仍然是缓冲的,流不会丢失它.但是你应该停止写作.这是为了允许背压传播.

您的问题在writable.write(chunk[, encoding][, callback])文档中得到解决:

这个返回值是严格的建议.你可以继续写,即使它返回false.但是,写入将缓冲在内存中,因此最好不要过度执行此操作.相反,'drain'在写入更多数据之前等待 事件.


Tac*_*tex 15

当#write方法返回false时,您尝试写入的数据是否真的"被拒绝"?或者是缓冲(或其他)?

数据被缓冲.但是,write()在不允许缓冲区耗尽的情况下进行过多调用将导致高内存使用率,糟糕的垃圾收集器性能,甚至可能导致Node.js因Allocation failed - JavaScript heap out of memory错误而崩溃.看到这个相关的问题:

节点:fs write()不在内部循环中写入.为什么不?


作为参考,以下是highWaterMark当前文档(v8.4.0)的一些相关详细信息和背压:

writable.write()

true如果内部缓冲区小于highWaterMark在录入后创建流时配置的内部缓冲区,则返回值chunk.如果false返回,则应该停止进一步尝试将数据写入流中,直到'drain'发出该事件.

当流没有耗尽时,调用write()将缓冲chunk并返回false.一旦所有当前缓冲的块被耗尽(接受由操作系统传递),'drain'将发出该事件.据建议,一旦write()返回false,没有更多的块写入,直到'drain'事件被发出.在write()允许调用未耗尽的流时,Node.js将缓冲所有写入的块,直到最大内存使用量发生,此时它将无条件地中止.即使在它中止之前,高内存使用将导致糟糕的垃圾收集器性能和高RSS(即使在不再需要内存之后,通常也不会将其释放回系统).

流中的背压

在数据缓冲区已超过highWaterMark或写入队列当前正忙的任何情况下,.write()将返回false.

false返回一个值时,背压系统启动.它将暂停传入的Readable流发送任何数据并等待消费者再次准备就绪.清空数据缓冲区后,.drain()将发出一个事件并恢复传入的数据流.

队列完成后,背压将允许再次发送数据.正在使用的内存空间将自行释放并为下一批数据做好准备.

               +-------------------+         +=================+
               |  Writable Stream  +--------->  .write(chunk)  |
               +-------------------+         +=======+=========+
                                                     |
                                  +------------------v---------+
   +-> if (!chunk)                |    Is this chunk too big?  |
   |     emit .end();             |    Is the queue busy?      |
   +-> else                       +-------+----------------+---+
   |     emit .write();                   |                |
   ^                                   +--v---+        +---v---+
   ^-----------------------------------<  No  |        |  Yes  |
                                       +------+        +---v---+
                                                           |
           emit .pause();          +=================+     |
           ^-----------------------+  return false;  <-----+---+
                                   +=================+         |
                                                               |
when queue is empty     +============+                         |
^-----------------------<  Buffering |                         |
|                       |============|                         |
+> emit .drain();       |  ^Buffer^  |                         |
+> emit .resume();      +------------+                         |
                        |  ^Buffer^  |                         |
                        +------------+   add chunk to queue    |
                        |            <---^---------------------<
                        +============+
Run Code Online (Sandbox Code Playgroud)

  • @Interaction:我从 Node.js 文档中复制粘贴了图表[此处](https://nodejs.org/en/docs/guides/backpressuring-in-streams/#lifecycle-of-pipe)。 (2认同)