О разработке на node.js … Часть 2

в 7:55, , рубрики: node.js, охотничьи байки, метки: ,

часть 1

В первой части шла речь о «проверенных временем» решениях, утянутых домовитыми лапами на ноду. Теперь немного лисапедов и пара мыслей вслух.

puncher (генератор timeline-ов)

Адрес — github.com/nodeca/puncher

Если вам не безразлична производительность, ее желательно контролировать во время разработки. Один из самых простых способов — расставить по коду таймстампы. Это ни в коем случае не «единственный» и не «самый лучший» способ. Просто он часто удобен на самом раннем этапе, когда пишется код и делается ревью — не нужно заниматься сбором статистики, и вообще «далеко ходить». Достаточно вывести прямо внизу страницы таймлайн, и сразу будет видно, что не так. Естественно, потом полезно в любом случае смотреть логи медленных запросов и собирать прочую статистику профайлером. Но проблемных мест останется на порядок меньше. И выясните вы это сразу на ревью, а не когда-нибудь потом, когда мозг уже переключился на другую задачу.

Сам пример можно посмотреть тут, отмотав страницу в самый конец: dev.nodeca.com/. Данные генерируются самодельным модулем puncher. Он позволяет очень легко расставить по коду вложенные интервалы. Это не бенчмарк. Это именно инструмент, который позволяет обнаружить проблемы на самом раннем этапе. Ну и сделать некоторые выводы о том, чего вообще ожидать. При некоторой ловкости, достаточно 1-2 таймлайнов, чтобы увидеть профуканные индексы и медленный код.

Библиотека простая, но не совсем волшебная :). Пересекающиеся интервалы для «параллельных» вызовов, она не просекает. Но, как говорят наши друзья из яблочной компании, «вам этого не нужно» :). У нас сначала были параллельные вызовы, потом я посмотрел таймлайн — увидел нулевую экономию, и выстроил все водопадом, чтобы не возиться.

babelfish (i18n)

Адрес — github.com/nodeca/babelfish

Это второй после пунчера велосипед — попытка радикально упростить интернационализацию. Конечно, я в курсе про gettext(). Но мысль о клепании пучка фраз на каждую плюрализацию навевала тоску и вгоняла в депрессию. Захотелось указывать плюральные формы сразу в одной строке, и вообще, клепать фразы легким движением бровей. Возможно, это слегка отдает кодингом, но не выходит за рамки добра и зла. Главные критерии — простота набивки и перевода. Вот реальный пример: github.com/nodeca/nodeca.forum/blob/master/config/locales/general.en-US.yml. С моей точки зрения, такой файл вполне реально отдавать переводчику. Ну и программистов тоже не напрягает.

Естественно, библиотека рассчитана на работу как на сервере так и на клиенте. Правила плюрализации сделаны и оттестированы для всех языков, найденных в юникодном стандарте.

Fontello

Адрес — github.com/fontello/fontello
Сайт — fontello.com/

Это симбиоз детской мечты и суровой реальности :). Не буду рассказывать про иконочные шрифты, потому что уже касался этой темы в других статьях. Просто отмечу, что по мере работы над нодекой, возникла побочная идея «сделать классную хрень». Ну а так как в сутках лишь 24 часа, то пришлось придумывать для самого себя отмазки, чтобы отвлечься от основного проекта. В итоге фонтелло используется в качестве песочницы, для проверки всяких завихрений мозга. Уже было несколько раз, что некоторые идеи показали себя «не очень». На основном проекте ошибиться стоит дорого для нервов. А на фонтелле люди пишут багрепорты и помогают улучшить мир.

Из последнего — доделали инлайновое редактирование имен глифов, и привели генерируемые файлы к формату, удобному для скидывания в репозитори. Когда надо будет расширить фонт — просто импортируем конфиг и продолжаем работу. Для тех, кто предпочитает сам задавать @font-face, генерируются файлы, состоящие только из кодов символов. В общем, процесс оптимизирован под разработчиков, любящих автоматизацию и гитхаб.

Кстати, на днях со мной связался Daniel Bruce, автор Entypo. Ожидается большое обновление.

Nodeca

dev.nodeca.com/

Это штука, с которой все началось, и от которой приходилось все время отвлекаться :). Работа еще не закончена, но мы уже перестали «активно развивать нодовские библиотеки» — на данный момент архитектура почти стабилизировалась. Как обычно, тырили с миру по нитке творчески переосмысливали удачные решения, где возможно, и сочиняли свое, где позволял опыт. Наверное, можно сказать, что мы сделали свой «фреймворк». Просто времени его продвигать особо нет. Но кого интересуют подходы — можно посмотреть текущую документацию, сделанную для внутренних нужд: github.com/nodeca/nodeca/tree/master/docs/nodeca-technical. Так как проект еще не готов, и код местами «прототипного» качества, то просто расскажу о некоторых проверенных вещах и практических выводах.

Кусочки из Socket Stream

Отсюда мы взяли древовидную идею адресации серверных методов, (вместо controller/action). Изначально хотелось использовать SS напрямую, но там не были проработаны моменты с серверным рендерингом, разбивка на несколько подпроектов и т.п. В общем, сам по себе socketstream интересен, и почерпнуть там было чего. Но не целиком :)

Из довольно интересной концепции была передача данных по вебсокетам. С виду это выглядело красивым, но на практике оказалось большой жопой, что данные не пакуются. То есть, JSON для генерации на клиенте занимал в несколько раз больше (!) чем целиком отрендеренный HTML, пожатый в gzip. Можно конечно было подсуетиться с ручной перепаковкой, но это отдавало серьезным маразмом. К тому же, вебсокеты расчитаны не на бинарные данные, а на utf-8, поэтому внутри все заворачивается в base64, и мы пролетаем на 30% по сравнению с аяксом.

Второй проблемой с вебсокетами стало несовершенство библиотек. Наблюдались странные «провисания», что абсолютно неприемлимо при кликах на элементах интерфейса. Это все, конечно, не высосано из пальца, а было обнарушено на фонтелле и текущей нодековской тдемке. Наверное, если упереться рогом в землю и потратить прилично времени, можно было бы все свести к боевой ничьей. Но смысл? В итоге, для обычных RPC-запросов откатили все на аякс, а FAYE/Socket.io отложили именно для подписок, где размер данных и задержки не особо актуальны (или по крайней мере не так заметны).

Из удачно стырепозаимствованных концепций оказались рельсовские before/after фильтры. С учетом специфики — рекурсивности (для дерева) и коллбеков. В коде выглядит вот так: github.com/nodeca/nodeca.forum/blob/master/server/forum/thread.js#L57. Привычная классика, но в нодовском стиле.

Mongoose

Этой ODM нода несомненно может гордиться. Отличная библиотека. Но есть нюанс. На выборке кастинг документов работает ОООЧЕНЬ МЕЕЕЕДЛЕННО. К счастью, в версии 3.0 добавили опцию {lean: true}, которая отключает нафик всю магию (и удобства) и выдает данные из драйвера как есть.

В итоге получается так:

  • Там где не важна скорость, но надо быстро клепать тонны запросов (например, в админке) — используем монгуз во всей красе.
  • На тех запросах (а их немного), где скорость принципиальна, отключаем нафик кастовалку документов, но продолжаем использовать монгузовский генератор запросов (там нет заметного оверхеда).

Профит! Раньше для «быстрых» запросов надо было брать другие библиотеки или вручную лезть к монговскому драйверу. Теперь проблем нет.

(*) Кто успел проапгрейдиться на могу 2.2 — монгуз поддерживает агрегацию, и там сразу выдает некастованные объекты.

Из важных замечаний — не буду клясться на 100%, но сложилось впечатление, что создание больших объектов в javascript — не совсем бесплатная операция. Фильтрация полей во время запроса дает довольно большой прирост скорости выборки. При том что в консоли монги время не менялось. Тормоза спрятались где-то в передачи данных из монги в v8. Я был бы очень признателен, если бы кто-нибудь прояснил этот момент.

Общие шаблоны на сервере и клиенте

Это одна из самых крутых фишек ноды. Если она вам нужна :). Будут говорить про асинхронность и скорость — не верьте. В других платформах есть сравнимые решения. Но без проблем заколбасить универсальные разухабистые шаблоны (с хелперами и т.п.) и на сервере и на клиенте можно только при использовании ноды. Если вам действительно нужна такая фишка — он ноды может быть смысл. Если нет — возможно, вам будет быстрее и интереснее писать на другом языке. Я не сторонник маниакального сования ноды везде где можно.

В общем, это была лирика, а теперь возвращяемся к рендерингу и идем за примером опять сюда: dev.nodeca.com/. Если в современом браузере потыкать по ссылкам и посмотреть вниз, то вы обнаружите, что в таймлайне пропала операция рендеринга. Это та самая военная хитрость — рендеринг идет на клиенте, а location подкручивается через history api. Профит заключается в том, что не надо отдельно думать над индексацией, лепить под гугла шибанговский клозет, и горевать, что это не работает в яндексе. Ну и конечно же, программисту теперь вообще не надо думать, где именно отрендерится его шаблон.

Насчет скорости шаблонизации стоит поговорить отдельно. Лично я предпочитаю Jade. В стоковом режиме он не самый быстрый. Но есть две волшебных опции, которые все меняют :)

{ compileDebug: false, self: true }

Первая отключает генерацию отладочной информации. Вторая генерирует код без `with` — но вам прийдется добавлять `self.` ко всем именам переменных, переданным в шаблон. Вполне приемлимая цена. На страницах нодеки выигрыш по скорости оказался ~ в полтора-два раза (изначально таймлайн показывал, что рендеринг выедал 50-70% от запроса). Так что результат вполне полезный. compileDebug еще как-то можно найти в документации (если догадаетесь, что надо искать), а вот про self — уже не факт. Поэтому считаю не зазорным поделиться «находкой».

Для тех, кому интересно, могу еще предложить ссылку на «тесты» jsperf.com/template-without-precompile/8. Слово тесты взял в кавычки, потому что в реальной жизни результат немного другой :). Сколько бы миллионов попугаев ни показывали пузомерки, в шаблонах все равно останутся вызовы i18n, конверторов дат и прочих хелперов, которые радикально изменят результат. Поэтому советую проверять чем-то вроде пунчера на реальных страницах.

Пока всё. Надеюсь, это будет кому-то полезно :). Когда появятся новости про нодеку — напишу еще раз.

PS. Если кто-нибудь захочет поучаствовать в разработке нодеки — есть пара вакансий.

Автор: Vitaly

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js