Подводные камни при разработке крупных приложений на Node.js

This post is also available in: Английский

Уже стало понятно, что Node.js — яркий тренд в сообществе web-разработчиков. В данной статье мы попытаемся затронуть следующие вопросы: какие подводные камни могут ожидать разработчика, и стоит ли вообще переходить на нее.

В основном Node.js ассоциируется у разработчиков с небольшими сервисами народе чата. (подробнее в статье «Разработка высокопроизводительных сервисов на Node.js»). На то есть веские причины. Нода отлично подходит для сервисов, которые поддерживают постоянное подключение к клиенту, либо долгое время ожидают ответа от него. Отсутствие блокировки ввода/вывода позволит читать большие файлы, оставляя при этом ваш сервис готовым к обработке новых запросов.

Итак, вот за что сообщество так любит Node.js:

• Нода может держать тысячи открытых соединений с клиентами, параллельно с этим осуществляя запись в БД.
• Событийная модель работы отлично подходит для каскадов асинхронных запросов к другим частям приложения.
• Большое и отзывчивое сообщество, а также быстрая скорость разработки самой технологии.
• Множество отличных модулей.
• JSON — родной формат описания объектов. А при использовании MongoDB вообще отпадает вопрос сериализации.

Кстати, нода позволяет также быстро реализовать и высоконагруженные REST-сервисы. А такие модули как express существенно упрощают эту задачу.

Тем не менее у каждой медали есть и обратная сторона.

Неблокиремый поток?

Мнение, что node.js невозможно заблокировать, ошибочно. Архитектурно нода устроена таким образом, что все выполняется в единственном потоке. Соответственно тяжелые математические вычисления спокойно повесят приложение на какое-то время.

Как же с этим бороться?  Существует несколько подходов к решению данной проблемы. Все они основаны на идее вынесения тяжелые вычисления из основного потока исполнения (Control Flow). Итак, вот они:

  1. Разбить выполнение кода на итерации и выполнять их через функцию setTimeout с задержкой в 0 мс. Стоит понимать, что 0 абсолютно не значит, что задержка будет отсутствовать. Это означает, что нода начнет исполнять код так скоро, как сможет. Конечно, придется где-то сохранять промежуточные результаты. Также стоит упомянуть и об увеличении длительности выполнения этих самых вычислений.
  2. Вынести исполнение в воркеры. По сути такой подход породит дополнительные процессы в системе, в которых и выполнится наш код.

Чтение/запись в файлы и возникновение коллизий

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

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

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

Блокировка исполнения при работе Garbage Collector.

Как известно, Node.js  построена на базе движка V8. Его особенность заключается в том, что при запуске GC нода останавливается до окончания его работы. Время задержки напрямую зависит от числа объектов в куче. «Текущие» приложения теряют в производительности именно из-за работы garbage collector. Результаты работы сборщика мусора можно вывести, запустив ноду следующим образом:

node <script> —trace-gc

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

Таким образом, аккуратное и вдумчивое создание объектов должно стать вашей основной парадигмой при написании сервисов на Node.js.

Подводные камни при отладке.

Отладка ноды — непростая задача. Конечно, можно просто запустить ноду с в debug-режиме. Но удобство отладки приложения в нем довольно низкое. Гораздо проще использовать сторонние приложения:

1) node-inspector

Этот продукт позволяет удаленно отлаживать ноду в интерфейсе WebInspector, редактировать runtime-код и даже профилировать приложение.

Ниже небольшое видео про отладку ноды при помощи node-inspector

2)  PHPStorm/WebStorm Node.js Debugger

Данные редакторы имеют встроенный функционал для локальной и удаленной отладки ноды в своей собственной среде.

Событийная модель, заложенная в Node.js может легко запутать разработчика. Вывод стека, состоящего из последовательных вызовов callback-функций как правило абсолютно не информативен. Тем не менее можно повысить читаемость вывода если принять за правило все коллбекам давать имена. Например:

1
2
3
setTimeout(function do() {
  /* Some code here */
});

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

Каскады callback-функций

Сложные приложения на Node.js подразумевают последовательный вызовов множества коллбеков. Часто в коде можно увидеть следующие конструкции:

1
2
3
4
5
6
7
8
9
do(function () {
  ...  
  function() {
    ...
    function() {
      ...
    }
  }
});

Читать и поддерживать такой код крайне сложно. Для организации кода при таких ситуациях используются специальные паттерны. Подробнее о них можно прочитать в статье «Node.js Control flow».

Изменчивое API

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

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

npm install <module>

Примеры успешного применения Node.js

На GitHub расположен целый список компаний, которые используют ноду в своих сервисах. ознакомится с ним можно по этой ссылке.

Мы остановимся на некоторых из них:

  • Yahoo реализовали на ноде фреймворк Mojito, позволяющий писать приложения, работающие как на клиентской, так на серверной стороне, в зависимости от того, какое устройство из запускает.
  • Yammer использует сервис на Node.js для кросс-доменного проксирования запросов к API. В качестве основного преимущество разработчики из Yammer приводят возможность ноды обслуживать множество одновременных запросов.
  • Bocoup написали на ноде IRC_бота. Выбор в пользу этой технологии был сделан исходя идеи использования JavaScript на стороне сервера (У Bocoup много JS-разработчиков)

Резюме

Использовать или не использовать Node.js — решать только вам, исходя из специфики конкретной задачи. Но стоит понимать, что у нее есть и сильные, и слабые стороны.

Полезные ссылки

One thought on “Подводные камни при разработке крупных приложений на Node.js

  1. Если честно, то Node.js всё таки фиговенький выбор. Ничего, конечно, но так себе.

    Серьезных проблем несколько.

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

    Во-вторых, node.js не умеет использовать ядра. Это значит, что если начинаем упираться в перфоманс, то надо мудрить синхронизации между разными инстансами.

    В-третьих, в node.js всё очень сыро с шедулингом дисковых операций. Это очень большая и болезненная тема. Эрливидео сейчас может раздавать с диска много гигабит VOD и это возможно только благодаря очень mature системе шедулинга дисковых операций.

    В-четвертых, node.js не умеет шедулиться вне I/O. Это на самом деле серьезная проблема, когда начинается массовая обработка пользователей. Так, например, эрливидео начинает достаточно плавно деградировать в скорости обработки одного запроса (что терпимо для HDS/HLS) при утыкании в предел по какому-то ресурсу. Это в частности достигается за счёт вытесняющей многозадачности.

    В-пятых, в node.js как и в джаве и в прочих подобных языках нет, не было и в ближайшее время за разумные деньги не появится сборщика мусора без stop world GC. На практике это означает по секунде или больше выпадания сервера при полной очистке кучи в 10 и больше гигабайт. В эрланге такого не бывает.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Пожалуйста напечатайте буквы/цифры изображенные на картинке

Please type the characters of this captcha image in the input box