Как не нужно использовать Node.js Stream API
- воскресенье, 28 октября 2018 г. в 00:18:47
В интернете опять кто-то не прав – во вчерашнем Node Weekly была ссылка на пост в котором автор пытается измерить и сравнить с "аналогами" производительность Stream API в Node.js. Грусть вызывает, то как автор работает со стримами и какие он выводы он пытается на основе этого сделать:
...this worked pretty well on smaller files, but once I got to the biggest file, the same error happened. Although Node.js was streaming the inputs and outputs, it still attempted to hold the whole file in memory while performing the operations
Давайте попробуем разобраться, что не так с выводами и кодом автора.
С моей точки зрения проблема в том что автор статьи не умеет пользоваться Stream’ами и это проблема с которой приходиться довольно часто сталкиваться. У этого явления есть, на мой взгляд, три причины:
Все вместе это приводит к тому, что разработчики довольно часто не умеют и не хотят использовать Stream API.
Что не так с кодом автора?
Для начала повторим тут задачу(оригинал на английском и ссылку на файл можно найти в посте):
Есть некий файл размером 2.5 ГБ со строками вида:
C00084871|N|M3|P|201703099050762757|15|IND|COLLINS, DARREN ROBERT|SOUTHLAKE|TX|760928782|CELANESE|VPCHOP&TECH|02282017|153||PR2552193345215|1151824||P/R DEDUCTION ($76.92 BI-WEEKLY)|4030920171380058715
Его нужно распарсить и узнать следующую информацию:
В чем проблема? – Автор честно говорит, что загружает весь файл в память и из-за этого Node “вешается” и автор приводит нам интересный факт.
Fun fact: Node.js can only hold up to 1.67GB in memory at any one time
Автор делает из этого факта странный вывод, что это Stream’ы загружают весь файл в память, а не он написал неправильный код.
Давайте опровергнем тезис: "Although Node.js was streaming the inputs and outputs, it still attempted to hold the whole file", написав небольшую программу, которая посчитает количество строк в файле любого размера:
const { Writable } = require('stream')
const fs = require('fs')
const split = require('split')
let counter = 0
const linecounter = new Writable({
write(chunk, encoding, callback) {
counter = counter + 1
callback()
},
writev(chunks, callback) {
counter = counter + chunks.length
callback()
}
})
fs.createReadStream('itcont.txt')
.pipe(split())
.pipe(linecounter)
linecounter.on('finish', function() {
console.log(counter)
})
N.B.: код намерено написан максимально просто. Глобальные переменные это плохо!
На что стоит обратить внимания:
Ну что ж, испытаем наше творение на большом файле:
> node linecounter.js
13903993
Как видим – все работает. Из чего можем сделать вывод что Stream API прекрасно справляется с файлами любого размера и утверждение автора поста, мягко говоря, не верно. Приблизительно также мы можем посчитать любое другое значение требуемое в задаче.
Расскажите: