Узел JS Streams: понимание объединения данных - PullRequest
0 голосов
/ 29 июня 2018

Одна из первых вещей, которую вы изучаете, когда смотрите на модуль 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 голосов
/ 30 июня 2018

Задача кода, который вы показываете, состоит в том, чтобы собрать все данные из потока в один буфер, чтобы при возникновении события end у вас были все данные.

request.on('data',...) может излучать только один раз или сотни раз. Это зависит от размера данных, конфигурации объекта потока и типа потока позади него. Вы никогда не сможете с уверенностью предположить, что он будет излучать только один раз.

Вы можете делать что-либо с потоком запросов, например, направлять его через некоторые преобразования в объектном режиме и через некоторые другие потоки чтения. Похоже, что этот шаблон объединения никогда не нужен.

Этот шаблон объединения используется только тогда, когда вы пытаетесь получить все данные из этого потока в одну переменную. Весь смысл передачи в другой поток состоит в том, что вам не нужно извлекать все данные из одного потока перед отправкой в ​​следующий поток. .pipe() будет просто отправлять данные по мере их поступления в следующий поток. То же самое для преобразований.

Это потому, что поток запросов при обработке тел POST и PUT почти всегда генерирует только одно событие данных, потому что их полезная нагрузка намного ниже предела размера раздела чанка ?.

Вероятно, потому что полезная нагрузка ниже некоторого размера внутреннего буфера, и транспорт отправляет все данные одновременно, и вы не работаете по медленной линии связи и ... Дело в том, что вы не можете делать предположения о том, как будет много событий данных. Вы должны предположить, что их может быть больше одного, и что первое событие данных не обязательно содержит все данные или данные, разделенные хорошей границей. Многое может привести к тому, что входящие данные будут разбиты по-разному.

Имейте в виду, что readStream читает данные до тех пор, пока на мгновение не останется больше данных для чтения (до размера внутреннего буфера), а затем выдает событие data. Он не ждет, пока буфер заполнится, прежде чем выдать событие data. Таким образом, поскольку все данные на нижних уровнях стека TCP отправляются в пакетах, все, что требуется, - это мгновенная задержка доставки с некоторым пакетом, и поток не найдет больше данных, доступных для чтения, и выдаст событие data. Это может произойти из-за способа отправки данных, из-за того, что происходит в транспорте, по которому передаются данные, или даже из-за локального управления потоком TCP, если много вещей происходит со стеком TCP на уровне ОС.

На практике, насколько большим должен быть кодированный в JSON-объект объект для потоковой передачи более чем в один блок данных?

Вы действительно не должны знать или заботиться, потому что вы ДОЛЖНЫ предполагать, что объект любого размера может быть доставлен более чем в одном data событии. Вы, вероятно, можете с уверенностью предположить, что объект JSON, превышающий размер буфера внутреннего потока (который можно узнать, изучив код потока или изучив внутренние компоненты в отладчике), будет доставлен в нескольких событиях данных, но вы не можете предполагать обратное, потому что другие переменные, такие как связанные с транспортом вещи, которые могут привести к его разделению на несколько событий.

Мне кажется, что потокам objectMode не нужно беспокоиться о конкатенации, потому что, если вы имеете дело с объектом, он почти всегда не больше одного блока данных, который атомарно преобразуется в один объект? Я мог видеть проблему, если клиент загружал что-то вроде огромной коллекции (когда поток был бы очень полезен, если бы он мог анализировать отдельные объекты в коллекции и выдавать их один за другим или пакетами).

Потоки в объектном режиме должны выполнять свою собственную внутреннюю буферизацию, чтобы найти границы любых объектов, которые они анализируют, чтобы они могли излучать только целые объекты. На некотором низком уровне они объединяют буферы данных, а затем проверяют их, чтобы увидеть, есть ли у них еще целый объект.

Да, вы правы, если бы вы использовали поток объектного режима, а сами объекты были очень большими, они могли бы потреблять много памяти. Вероятно, это не самый оптимальный способ работы с данными такого типа.

Имеют ли потоковые преобразования objectMode внутреннюю логику для автоматической конкатенации вплоть до границ объекта?

Да, они делают.


К вашему сведению, первое, что я делаю, когда делаю http-запросы, это использую библиотеку request-promise, чтобы мне не приходилось делать свою собственную конкатенацию. Это обрабатывает все это для вас. Он также предоставляет интерфейс на основе обещаний и около 100 других полезных функций, которые я считаю полезными.

...