节点JS流:了解数据串联

时间:2018-06-29 20:27:17

标签: node.js stream buffer

当您查看节点的http模块时,要学习的第一件事就是这种模式,用于连接来自请求读取流的所有数据事件:

let body = [];
request.on('data', chunk => {
  body.push(chunk);
}).on('end', () => {
  body = Buffer.concat(body).toString();
});

但是,如果您查看许多流媒体库实现,它们似乎完全掩盖了这一点。另外,当我检查request.on('data',...)事件时,对于具有几个到十几个属性的典型JSON有效负载,它几乎几乎只发出一次。

您可以对请求流进行处理,例如通过对象模式中的某些转换将其通过管道传递到其他读取流。似乎不需要这种级联模式。

这是因为处理POST和PUT主体的请求流几乎只发出一个数据事件,这是因为它们的有效载荷远低于块分区大小限制吗?实际上,需要在一个以上的数据块中流式传输JSON编码对象的大小?

在我看来,objectMode流无需担心级联,因为如果要处理一个对象,它几乎总是不大于一个发射数据的块,而该块会原子地转换为一个对象?我可以看到一个问题,如果客户端正在上载诸如大型集合之类的东西(在这种情况下,流将非常有用,只要它可以解析集合中的各个对象并逐个或分批地发送它们)。

我发现这可能是真正了解流的node.js细节最令人困惑的方面,在流传输原始数据与处理原子块(如对象)之间存在怪异的脱节。 objectMode流转换是否具有用于自动连接到对象边界的内部逻辑?如果有人可以澄清这一点,将不胜感激。

1 个答案:

答案 0 :(得分:3)

您显示的代码的工作是将流中的所有数据收集到一个缓冲区中,以便在发生end事件时,您便拥有了所有数据。

request.on('data',...)可能仅发射一次,也可能发射数百次。它取决于数据的大小,流对象的配置以及其背后的流的类型。您永远无法可靠地假设它只会发射一次。

  

您可以对请求流进行处理,例如通过对象模式中的某些转换将其通过管道传递到其他读取流。似乎不需要这种级联模式。

仅在尝试将所有数据从此流获取到单个变量中时才使用此串联模式。传递到另一个流的整个要点是,您无需在将数据发送到下一个流之前从一个流中获取整个数据。 .pipe()只会在数据到达下一个流时为您发送数据。变换也一样。

  

这是因为处理POST和PUT主体的请求流几乎只发出一个数据事件,这是因为它们的有效载荷远低于块分区大小限制吗?

这很可能是因为有效负载低于某个内部缓冲区大小,并且传输一次发送了所有数据,并且您没有在慢速链接上运行,并且....此处的要点是您无法对如何将会有许多数据事件。您必须假设可以有多个以上对象,并且第一个数据事件不一定包含所有数据或在合理边界上分离的数据。很多事情都会导致传入的数据以不同的方式分解。

请记住,readStream会读取数据,直到暂时没有更多数据要读取(取决于内部缓冲区的大小),然后它会发出data事件。它不会等到缓冲区填满后才发出data事件。因此,由于TCP堆栈较低层的所有数据都是以数据包形式发送的,因此只需要短暂的延迟就可以传输某些数据包,并且该流将找不到更多可读取的数据,并且会发出data事件。发生这种情况的原因可能是数据的发送方式,数据流传输中发生的事情,甚至是如果OS级别的TCP堆栈上发生了很多事情,甚至是由于本地TCP流控制。 / p>

  

实际上,需要在多个数据块中流式传输JSON编码对象的大小?

您真的不应该知道或关心,因为您必须假设任何大小的对象都可以在多个data事件中传递。您可能可以放心地假设,将在多个数据事件中提供大于内部流缓冲区大小(可以通过研究流代码或检查调试器中的内部数据来发现)的JSON对象,但是您不能假定相反的情况,因为还有其他变量,例如与运输相关的事物,可能导致它分解为多个事件。

  

在我看来,objectMode流无需担心级联,因为如果要处理一个对象,它几乎总是不大于一个发射数据的块,而该块会原子地转换为一个对象?我可以看到一个问题,如果客户端正在上载诸如大型集合之类的东西(在这种情况下,流将非常有用,只要它可以解析集合中的各个对象并逐个或分批地发送它们)。

对象模式流必须进行自己的内部缓冲以查找它们正在解析的任何对象的边界,以便它们只能发出整个对象。在某种程度上,它们将数据缓冲区串联起来,然后检查它们是否还有完整的对象。

是的,如果您使用的是对象模式流并且对象本身非常大,那么它们可能会占用大量内存,这是正确的。可能这不是处理此类数据的最佳方法。

  

objectMode流转换是否具有用于自动串联对象边界的内部逻辑?

是的,他们这样做。


仅供参考,在发出http请求时,我要做的第一件事就是使用request-promise库,这样我就不必自己进行串联了。它为您处理所有这一切。它还提供了一个基于Promise的界面以及大约100个其他有用的功能,这些功能我都觉得有用。