Node.jsstream如何工作?

我有一个关于Node.jsstream的问题 – 特别是他们如何在概念上工作。

不乏关于如何使用stream的文档。 但是我很难在数据层面find数据stream的工作原理。

我对Web通信(HTTP)的理解有限,就是数据的完整“包”被反复发送。 与订购公司目录的个人相似,客户端向服务器发送GET(目录)请求,服务器响应该目录。 浏览器没有收到目录的页面,而是整本书。

节点stream可能是多部分消息?

我喜欢REST模式 – 特别是它是无状态的。 浏览器和服务器之间的每一次交互都是完全独立和充分的。 节点stream是不是RESTful? 一位开发人员提到了与套接字pipe道的相似性,这使得连接保持开放。 回到我的目录订购的例子,这是否会像电视广告的“等等,还有更多! 而不是完整的目录?

大部分数据stream是接收方“下游”发送消息的能力,如上游的“暂停”和“继续”。 这些消息由什么组成? 他们是POST吗?

最后,我对Node工作方式的有限理解包括这个事件循环。 函数可以放置在线程池的不同线程中,并且事件循环继续。 但是,不应该发送数据stream保持事件循环占用(即停止),直到stream完成? 请问如何继续监视来自下游的“暂停”请求?事件循环是否将该stream放置在池中的另一个线程上,当遇到“暂停”请求时,检索相关线程并暂停它?

我已经阅读了node.js文档,完成了nodechool教程,构build了一个heroku应用程序,购买了两本书(真实的,自包含的,书籍,有点像以前说过的目录,可能不像节点stream),问几个“节点”教练在代码bootcamps – 都讲述如何使用stream,但没有人谈论下面实际发生的事情。

也许你遇到了一个很好的资源解释这些工作? 也许对于一个非CS的头脑来说,一个好的拟人化的比喻?

首先要注意的是:node.jsstream不限于HTTP请求。 HTTP请求/networking资源只是node.js中stream的一个示例。

stream对于可以在小块中处理的所有内容都很有用。 它们允许您以更小的块来处理潜在的巨大资源,这些资源可以更轻松地放入RAM中。

假设你有一个文件(几GB),并且想把所有的小写字母转换成大写字母并把结果写到另一个文件中。 天真的方法会读取整个文件使用fs.readFile (为简洁起见省略error handling):

 fs.readFile('my_huge_file', function (err, data) { var convertedData = data.toString().toUpperCase(); fs.writeFile('my_converted_file', convertedData); }); 

不幸的是,这种方法很容易压倒你的RAM,因为整个文件在处理之前必须被存储。 你也浪费宝贵的时间等待文件被读取。 以小块处理文件是否合理? 在等待硬盘提供剩余数据的同时,您可以立即开始处理第一个字节:

 var readStream = fs.createReadStream('my_huge_file'); var writeStream = fs.createWriteStream('my_converted_file'); readStream.on('data', function (chunk) { var convertedChunk = chunk.toString().toUpperCase(); writeStream.write(convertedChunk); }); readStream.on('end', function () { writeStream.end(); }); 

这种方法好得多:

  1. 你只会处理一小部分容易放进你的RAM的数据。
  2. 一旦第一个字节到达,就开始处理,不要浪费时间,而是等待。

一旦你打开streamnode.js将打开文件,并开始读取它。 一旦操作系统将一些字节传递给正在读取文件的线程,它将被传递给您的应用程序。


回到HTTPstream:

  1. 第一个问题在这里也是有效的。 攻击者可能会向您发送大量数据来压倒您的RAM,并取消(DoS)您的服务。
  2. 然而,第二个问题在这种情况下更为重要:networking可能非常慢(想想智能手机),并且可能需要很长时间直到客户端发送一切。 通过使用stream,您可以开始处理请求并缩短响应时间。

在暂停HTTPstream时:这不是在HTTP级别完成的,而是低一些。 如果你暂停streamnode.js将停止从底层TCP套接字读取。 发生了什么是由内核决定的。 它仍然可以caching传入的数据,所以一旦你完成你目前的工作,它就准备好了。 它也可能通知发送者在TCP层面它应该暂停发送数据 。 应用程序不需要处理。 这不关他们的事。 事实上,发件人应用程序可能甚至不知道你不再主动阅读!

所以基本上是要尽快提供数据,而不是压倒你的资源。 底层的努力工作是由操作系统(例如netfshttp )或者你正在使用的stream的作者(例如zlib是一个Transformstream,通常用螺栓连接到fsnet )完成的。

我想你已经在过多地讨论所有这些工作,我喜欢它。

什么stream是好的

stream有两个好处:

  • 当一个操作很慢,它可以给你部分结果,因为它得到它们。 比如读一个文件,速度很慢,因为硬盘驱动器速度很慢,在读取文件时会给你一部分文件。 使用stream,您可以使用这些文件的部分,并开始立即处理它们。

  • 他们也很好地连接程序(阅读function)。 就像在命令行中一样,您可以将不同的程序组合在一起以产生所需的输出。 例如: cat file | grep word cat file | grep word

他们如何在引擎盖下工作…

大部分这些需要时间处理的操作,并且可以给你一些部分的结果,这些操作不是由Node.js完成的,而是由V8 JS引擎完成的,它只把这些结果交给JS来处理。

要了解你的http示例,你需要了解http的工作方式

有一个网页可以发送不同的编码。 一开始只有一种方法。 在请求时发送整个页面的地方。 现在它有更高效的编码来做到这一点。 其中之一就是在网页的一部分被发送到整个页面被发送的地方。 这是很好的,因为网页可以按照收到的方式进行处理。 想象一下networking浏览器。 它可以在下载完成之前开始呈现网站。

你的.pause和.continue问题

首先,Node.jsstream只能在同一个Node.js程序中工作。 Node.jsstream不能与另一台服务器甚至程序中的stream进行交互。

这意味着在下面的例子中,Node.js不能和Web服务器通信。 它不能告诉它暂停或恢复。

Node.js <-> Network <-> Webserver

真正发生的事情是,Node.js要求一个网页,并开始下载它,并没有办法停止下载。 只要放下sockets。

那么,在Node.js .pause或.continue中做什么呢?

它开始缓冲请求,直到你准备开始再次使用它。 但下载从未停止。

事件循环

我有一个完整的答案准备解释如何事件循环的作品,但我认为你最好看这个演讲 。

下面的图表似乎是节点stream类的相当精确的10.000英尺概述/图。

它代表着Chris Dickinson的贡献。

在这里输入图像描述