Потоки записи
Все потоки записи должны реализовывать абстрактный класс stream.Writable.
Данный класс имеет следующую событийную модель:
stream.Writable
^ ^
| |
fs.WriteStream http.ServerResponse
Любой наследник гарантирует, что он предоставит событийную модель родителя. Можно заметить, что данная событийная модель не похожа на модель чтения.
Вот пояснение модели:
1. Мы пробуем записать часть наших данных, если данные записались из буфера сразу в место предназначения в ответ мы получаем тру, можем снова пытаться записать следующую часть.
2. Если же у нас данные из буфера не попали сразу в место предназначения, а стали ожидать своей очереди, то райт вернет фолс. Теперь мы подписуемся на событие потока drain(буфер отдренировался), вместо продолжать заполнять буфер, который рискует в таком случае переполниться, что означает для нас, что мы можем попытаться записать следующую часть, в момент когда, буфер слил все в место предназначения.
3. Когда мы заносим последний чанк данных это следует делать местодм end(...)
Дальше приведем пример, файлового сервера, проблемой для которого является считывание за один раз громадного файла в память, или обслуживание довольно большого количества клиентов с медленным соединением, который не очень быстро принимают передаваемые байты. Все эти варианты заставляют нас рисковать переполнить память нашего сервера.
Поэтому нужно поступать хитрее:
Но в ноде уже есть более оптимизированная реализация данного алгоритма, поэтому выше написанный код, можно заменить на более короткий и оптимальный:
Все потоки записи должны реализовывать абстрактный класс stream.Writable.
Данный класс имеет следующую событийную модель:
Можно заметить что stream.Writable имеет событие завершения finish, но stream.Readable имел end. Это для того чтобы обеспечить различие дуплексный поток, одновременный вывод и ввод.
Вот пример некоторых наследников данного класса
^ ^
| |
fs.WriteStream http.ServerResponse
Любой наследник гарантирует, что он предоставит событийную модель родителя. Можно заметить, что данная событийная модель не похожа на модель чтения.
Вот пояснение модели:
1. Мы пробуем записать часть наших данных, если данные записались из буфера сразу в место предназначения в ответ мы получаем тру, можем снова пытаться записать следующую часть.
2. Если же у нас данные из буфера не попали сразу в место предназначения, а стали ожидать своей очереди, то райт вернет фолс. Теперь мы подписуемся на событие потока drain(буфер отдренировался), вместо продолжать заполнять буфер, который рискует в таком случае переполниться, что означает для нас, что мы можем попытаться записать следующую часть, в момент когда, буфер слил все в место предназначения.
3. Когда мы заносим последний чанк данных это следует делать местодм end(...)
Дальше приведем пример, файлового сервера, проблемой для которого является считывание за один раз громадного файла в память, или обслуживание довольно большого количества клиентов с медленным соединением, который не очень быстро принимают передаваемые байты. Все эти варианты заставляют нас рисковать переполнить память нашего сервера.
Поэтому нужно поступать хитрее:
function sendFile(filePath, res) {
var mime = require('mime').lookup(filePath); // npm install mime
var file = new fs.ReadStream(filePath);
file.on('readable', write);
function write() {
var fileContent = file.read();
if (fileContent && !res.write(fileContent)) {//если буфер записи сливается на клинет быстро, мы не попадем в if, а будем и дальше записывать по мере прочтения чанков
file.removeListener('readable', write); // пока не очищать буфер чтения, его ведь некуда сливать, будем захламлять только память
res.once('drain', function() {
file.on('readable', write);
write();
});
}
}
file.on('end', function(){//дочитали
res.end();//значит закрываем соединение с клиентом, мы отправили все что должны были
});
}
Но в ноде уже есть более оптимизированная реализация данного алгоритма, поэтому выше написанный код, можно заменить на более короткий и оптимальный:
function sendFile(filePath, res) {
var mime = require('mime').lookup(filePath); // npm install mime
var file = new fs.ReadStream(filePath);
file.pipe(res);//pipe есть у всех Readable потоков
file.pipe(process.stdout);//кроме того пайпить мы можем в любое количество потоков одновременно
file.on('error', function(err) {
res.statusCode = 500;
res.end('Server Error');
console.log(err)
});
//если оставить код без кода написанного ниже в этом блоке,
//то в моменты когда клиент будет обрывать соединение,
// не дождавщись всего файла, у нас закроется соединение записи,
// но файл чтения останется открытым, держа буферы и замыкания в памяти.
// при нормальном завершении соедиения, ответ сгенерирует finish, который
// нам приносит класс stream.Writable.
res.on('close', function(){//срабатывае, когда соединение обрывают
file.destroy();//обеспечиваем закрытие файла чтения, и всех контекстов из замыкания
});

Комментариев нет:
Отправить комментарий