21. Уроки Node.js. Writable Поток Ответа res, Метод pipe. Pt.2

lesson21pt2

По окончании файла наступит событие end, в обработчике которого мы завершим ответ вызовом res.end. Таким образом, будет закрыто исходящее соединение, потому что файл полностью отослан. Получившийся код является весьма универсальным:

Он реализует достаточно общий алгоритм отправки данных их одного потока в другой, используя самые стандартные методы потоков readable и writable. Об этом, конечно же, подумали и разработчики Node.js и добавили его оптимизированную реализацию в стандартную библиотеку потоков.

Соответствующий метод называется pipe. Посмотрим на пример:

Он есть у всех readable потоков и работает так: readable.pipe (куда писать, destination). Кроме того, что это всего лишь одна строка, то есть еще один бонус, например, можно один и тот же входной поток pipe (“пайпить”) в несколько выходных:

Например, кроме ответа клиенту будем выводить его еще стандартный вывод процесса:

Итак, запускаем. Вывелось одновременно и в браузере, и в console. Готов ли этот замечательный код к промышленной эксплуатации? Есть ли еще какие-то нюансы, которые нужно учесть?

Первым делом в глаза должно броситься отсутствие работы с ошибками. Если вдруг файл не найден или что-то с ним еще не так, тогда упадет весь сервер. Это не то, что нам нужно. Поэтому добавим, например, такой обработчик

Теперь мы немножко ближе к реальной жизни, и в ряде руководств такой код выдается вполне нормальный, но на самом деле это не так. Ставить такой код на живой сервер ни в коем случае нельзя. В чем же дело? Для того, чтобы продемонстрировать проблему, добавим дополнительные обработчики на события open и close  для файла.

Запускаем. Обновляю страницу. Обновляем несколько раз и смотрим в console. Заметьте, файл перезагружается и совершенно нормально то, что файл открывается, потом он целиком отдается и закрывается.

А теперь откроем console и запустим утилиту curl, которая будет скачивать вот этот url:

http://localhost:3000/big.html

с ограничением скорости 1кб/сек:

curl --limit-rate 1k http://localhost:3000/big.html

Если вы работаете под Windows, то эту утилиту можно легко найти и установить. Запускаем. Открывается файл и начинается получение. С виду все хорошо.

Жмем Ctrl+С, прекращаю загрузку. Обратите внимание, никакого close нету. Давайте еще раз. Получается, что если клиент открыл соединение, но закрыл его до того, как загрузка файла была завершена, то файл останется подвисшим.

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

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

Событие, которое нас интересует, называется  res.on('close'). Это событие отсутствует в обычном stream.Writeable, то есть, это именно расширение стандартного интерфейса потоков. Также, как у файлов, есть close, так и у объекта ответа ServerResponse тоже есть close. Но смысл второго close сильно отличается от смысла первого, описанного выше. Это очень важно, потому что на файловом потоке close это нормальное завершение (файл закрывается всегда в конце), а для объекта ответа close – это сигнал, что соединение было оборвано. При нормальном завершении происходит не close, а  finish. Итак, если соединение было оборвано,  то нам нужно закрыть файл и освободить его ресурсы, поскольку файл нам больше передавать некому. Для этого мы вызываем метод потоков file.destroy:

Теперь все будет хорошо. Давайте еще раз проверим. Запускаем. Теперь наш код можно пускать на живой сервер.

Код урока вы сможете найти в нашем репозитории.

1140_pyramids_and_sphinx_of_egypt_556884029e546

Материалы для статьи взяты из следующего скринкаста.

We are looking forward to meeting you on our website soshace.com

About the author

Stay Informed

It's important to keep up
with industry - subscribe!

Stay Informed

Looks good!
Please enter the correct name.
Please enter the correct email.
Looks good!

Related articles

Уроки Express.js . Логгер, Конфигурация, Шаблонизация с EJS. Часть 2.

Favicon – это все connect Middleware, он смотрит, если url имеет вид favicon.ico, то он читает favicon и ...

3. Уроки Express.js. Шаблонизация с EJS: Layout, Block, Partials

В реальной жизни у нас обычно больше, чем один шаблон. Более того, если уж так ...

24.11.2016

Уроки Express.js. Основы и Middleware. Часть 2.

Всем привет! Давайте продолжим наш урок об основах Express и Middleware. Итог (добавим в ...

No comments yet

Sign in

Forgot password?

Or use a social network account

 

By Signing In \ Signing Up, you agree to our privacy policy

Password recovery

You can also try to

Or use a social network account

 

By Signing In \ Signing Up, you agree to our privacy policy