我对Node.js流有疑问-特别是它们在概念上如何工作。
不缺少有关如何使用流的文档。但是我很难找到流在数据级别如何工作。
我对Web通信HTTP的有限理解是,来回发送完整的数据“包”。类似于个人订购公司目录,客户端向服务器发送GET(目录)请求,然后服务器以目录进行响应。浏览器不会收到目录的页面,而是会收到整本书。
节点流可能是多部分消息吗?
我喜欢REST模型- 尤其是它是无状态的。浏览器与服务器之间的每一次交互都是完全自给自足的。因此,节点流不是RESTful的吗?一位开发人员提到与插座管的相似之处,插座使连接保持打开状态。回到我的目录订购示例,这就像是一条信息电视广告,其行为“但是,等等!还有更多!” 而不是完全包含的目录?
流的很大一部分是接收器“下游”向上游发送“暂停”和“继续”之类的消息的能力。这些消息由什么组成?他们是POST吗?
最后,我对Node工作原理的有限视觉理解包括此事件循环。可以将函数与线程池放在不同的线程上,然后事件循环继续进行。但是,在数据流完成之前,是否不应该发送数据流使事件循环占据(即停止)?它还如何监视下游的“暂停”请求?n事件循环是否将流放在池中的另一个线程上,并且当遇到“暂停”请求时,检索相关线程并暂停它?
我已经阅读了node.js文档,完成了nodeschool教程,构建了一个heroku应用,购买了两本书(真实,自含,书籍,有点像之前讲过的目录,可能不喜欢节点流),问了几个“节点”代码训练营的讲师- 所有人都在谈论如何使用流,但没有人谈论下面实际发生的事情。
也许您遇到了很好的资源,解释了它们如何工作?对于非CS头脑来说,也许是一个很好的拟人类比?
首先要注意的是:node.js流不限于HTTP请求。HTTP请求/网络资源只是node.js中流的一个示例。
对于所有可以小块处理的事物,流都是有用的。它们使您能够以较小的块处理潜在的巨大资源,从而更轻松地将其装入RAM。
假设您有一个文件(大小为数GB),并且想要将所有小写字母转换为大写字母并将结果写入另一个文件。天真的方法将使用读取整个文件fs.readFile(为简便起见,省略了错误处理):
fs.readFile
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(); });
这种方法更好:
打开流后,node.js将打开文件并开始从中读取。一旦操作系统将一些字节传递给正在读取文件的线程,它将被传递给您的应用程序。
回到HTTP流:
暂停HTTP流时:这不是在HTTP级别完成的,但是要低得多。如果您暂停流,node.js将仅停止从底层TCP套接字读取。然后,所发生的事情取决于内核。它仍然可以缓冲传入的数据,因此在您完成当前工作后就可以使用了。它还可以在TCP级别通知发送方它应该暂停发送数据。应用程序不需要处理这些。那不关他们的事。实际上,发件人应用程序甚至可能没有意识到您不再积极阅读!
因此,基本上,这是关于在可用时立即提供数据,但又不会占用您的资源。底层的辛勤工作是由操作系统或者进行(例如net,fs,http),或者通过流的作家,你正在使用(例如,zlib它是一个Transform流,并且通常用螺栓固定fs或net)。
net
fs
http
zlib
Transform