Я использую node v0.12.7 и хочу напрямую передавать данные из базы данных клиенту (для загрузки файла). Однако при использовании потоков я замечаю большой объем памяти (и возможную утечку памяти).
С помощью выражения я создаю конечную точку, которая просто передает читаемый поток в ответ следующим образом:
app.post('/query/stream', function(req, res) {
res.setHeader('Content-Type', 'application/octet-stream');
res.setHeader('Content-Disposition', 'attachment; filename="blah.txt"');
//...retrieve stream from somewhere...
// stream is a readable stream in object mode
stream
.pipe(json_to_csv_transform_stream) // I've removed this and see the same behavior
.pipe(res);
});
В процессе производства читаемый stream
извлекает данные из базы данных. Объем данных довольно большой (1M + строки). Я поменял этот читаемый поток фиктивным потоком (см. Код ниже), чтобы упростить отладку и замечать одно и то же поведение: мое использование памяти увеличивается на ~ 200 М каждый раз. Иногда сбор мусора вбивается, и память падает немного, но она линейно возрастает, пока у моего сервера не хватит памяти.
Причина, по которой я начал использовать потоки, заключалась в том, чтобы не приходилось загружать большие объемы данных в память. Ожидается ли такое поведение?
Я также замечаю, что при потоковой передаче мое использование процессора скачкообразно достигает 100% и блоков (что означает, что другие запросы не могут быть обработаны).
Я использую это неправильно?
Чистое чтение кода потока
// Setup a custom readable
var Readable = require('stream').Readable;
function Counter(opt) {
Readable.call(this, opt);
this._max = 1000000; // Maximum number of records to generate
this._index = 1;
}
require('util').inherits(Counter, Readable);
// Override internal read
// Send dummy objects until max is reached
Counter.prototype._read = function() {
var i = this._index++;
if (i > this._max) {
this.push(null);
}
else {
this.push({
foo: i,
bar: i * 10,
hey: 'dfjasiooas' + i,
dude: 'd9h9adn-09asd-09nas-0da' + i
});
}
};
// Create the readable stream
var counter = new Counter({objectMode: true});
//...return it to calling endpoint handler...
Update
Просто небольшое обновление, я так и не нашел причину. Моим первоначальным решением было использовать cluster для создания новых процессов, чтобы другие запросы все равно могли быть обработаны.
С тех пор я обновился до node v4. Несмотря на то, что использование процессора/памяти во время обработки все еще остается высоким, оно, похоже, устраняет утечку (это означает, что использование памяти возвращается).