在阅读Github上的一些代码后,似乎我误解了highWaterMark
概念是如何工作的。
在可写流的情况下,尽可能快地写入大量数据,这是我对生命周期的想法:
1)虽然未达到highWaterMark
限制,但流能够缓冲和写入数据。
2)如果达到highWaterMark
限制,则流不能再缓冲,因此#write方法返回false以告知您尝试写入的内容不会写入(从不)。
3)一旦流发出drain
事件,这意味着缓冲区已被清理,您可以从您所在的位置再次写入"拒绝"。
在我看来这很清楚也很简单,但看起来这并不完全正确(在第2步),你试图写的数据是真的"拒绝"当#write方法返回false时?或者是缓冲(或其他东西)?
对不起基本问题,但我需要确定!
答案 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(即使在不再需要内存之后也不会将其释放回系统)。
在数据缓冲区已超过
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
事件以了解何时再次写入。