流出highWaterMark的误会

时间:2016-03-04 16:48:49

标签: node.js

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

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

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

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

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

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

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

3 个答案:

答案 0 :(得分:16)

  

2)如果达到highWaterMark限制,则流不能缓冲   所以#write方法返回false让你知道那是什么   你试着写不会写(从不)。

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

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

  

此返回值严格提供咨询。你可以继续写,   即使它返回false。但是,写入将被缓冲在内存中,   所以最好不要过度这样做。相反,等待   在编写更多数据之前发生'drain'事件。

答案 1 :(得分:11)

  

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

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

Node: fs write() doesn't write inside loop. Why not?

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

writable.write()

  

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

     

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

Backpressuring in Streams

  

在数据缓冲区已超过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    |
                        |            <---^---------------------<
                        +============+

答案 2 :(得分:3)

即使调用返回| rn | day_t | collect | |----|------------|---------| | 19 | 2015-07-10 | 1028544 | | 41 | 2015-11-23 | 1024545 | | 62 | 2016-05-17 | 1027511 | | 82 | 2016-09-15 | 1006441 | (并在此之前缓冲在内存中),最终也会写入您对流write的任何数据。

false选项可以控制所使用的“缓冲区内存”量。一旦您撰写的金额超过指定金额,highWaterMark将返回write,以便您有机会停止撰写。您不必这样做:如果您没有停止,没有数据被丢弃,您最终会耗尽更多内存(重写数据会导致重复)。而且,正如您所提到的那样,您可以听取false事件以了解何时再次写入。