据我所知,Node的事件IO模型的一个后果就是无法告诉Node进程(例如)通过TCP套接字接收数据,阻止,一旦你连接了你的接收事件处理程序(或以其他方式开始侦听数据)。
如果接收方无法足够快地处理传入数据,则可能导致“无限并发”,从而节点内的节点继续尽可能快地从套接字读取数据,在事件上调度新的数据事件在套接字上循环而不是阻塞,直到进程最终耗尽内存并死掉。
接收方无法告诉节点减慢其读取速度,否则将允许TCP的内置流量控制机制启动并向发送方指示它需要减速。
首先,到目前为止我所描述的是准确的吗?有没有我错过了允许节点避免这种情况的东西?
Node Streams最引人瞩目的功能之一是自动处理背压。
AFAIK,可写流(tcp套接字)可以告诉它是否需要减速的唯一方法是查看socket.bufferSize
(表示写入套接字但尚未发送的数据量) )。鉴于接收端的节点总是尽可能快地读取,这只能表明发送方和接收方之间的网络连接速度慢,而不是指示接收方是否无法跟上。
那么其次,在这种情况下,Node Streams自动背压可以以某种方式工作以处理无法跟上的接收器吗?
似乎这个问题影响浏览器通过websockets接收数据,原因类似于websockets API没有提供一种机制来告诉浏览器减慢从套接字的读取速度。
Node(以及使用websockets的浏览器)在应用程序级别实现手动流量控制机制的唯一解决方案,明确告诉发送过程减速吗?
答案 0 :(得分:5)
要回答您的第一个问题,我相信您的理解并不准确 - 至少在流之间管道数据时是这样。实际上,如果您阅读了pipe() function的文档,您会发现它明确表示它会自动管理流程,以便"目标不会被快速可读的流所淹没。&#34 ;
pipe()的底层实现正在为您解决所有繁重的工作。输入流(Readable流)将继续发出data个事件,直到输出流(Writable流)已满。顺便说一句,如果我没记错的话,当您尝试写入当前无法处理的数据时,流将返回false。此时,管道将pause()可读流,这将阻止它发出更多数据事件。因此,事件循环不会填满和耗尽你的记忆,也不会发出简单丢失的事件。相反,Readable将保持暂停,直到Writable流发出drain事件。此时,管道将resume()可读流。
秘诀就是将一条流输送到另一条流中,这样可以自动控制背压。这有希望回答你的第二个问题,即Node可以并且确实通过简单的管道流自动管理它。
最后,实际上没有必要手动实现(除非您从头开始编写新流),因为它已经为您提供了。 :)
处理所有这一切并不容易,正如Node博客上宣布的那样,在Node中宣布了streams2 API。它是一个很好的资源,当然可以提供比我更多的信息。但是,从文档here和出于向后兼容性的原因,您应该知道一点并不完全明显:
如果你附加了一个数据事件监听器,那么它会将流切换到流动模式,数据一旦可用就会传递给你的处理程序。
因此,请注意,附加数据事件侦听器以尝试观察流中的某些内容将从根本上将流更改为旧的处理方式。问我how I know。