Тут мы подходим к важной теме SSR (server side rendering).
Здесь мои коллеги уже писали о SSR в хх habr.com/ru/company/hh/…
А также я косвенно упоминал это в моем выступлении в GetItConf: youtube.com/watch?v=bZfULA…
Из интересного: >>>
node.js не занимается походами на бекенды. Мы сделали SSR as a service. То есть есть питонячье приложение, которое ходит на бекенды, за сессией пользователя, и когда все данные готовы, оно формирует JSON и делает POST (PUT) запрос на Node.js сервер.
Урл у нас всегда один
/render
. Информация о реальном урле и данных лежит в body запроса.
Схематично это выглядит вот так:
На сервере крутится Koa (выбирали между express, koa и hapi). express не взяли — были проблемы в секьюрности, hapi не взяли — больше экспертизы в Koa было.
Кроме того, api koa отдалено похож на api нашего питон-приложения, за счет чего разработчикам проще было бы освоиться в новом окружении.
Каждый инстанс ноды запускает внутри себя Clusters по количеству ядер выделенных контейнеру: nodejs.org/api/cluster.ht…. Количество ядер определяется и прокидывается конфигом. Сама нода живет в docker контейнере. Менеджится и раскатывается это все через ansible плейбуки.
Код вызова рендера очень простой умещается на десяток строк вместе с обработкой ошибок: gist.github.com/xnimorz/2a6283…
То есть, что мы сделали: весь клиентский js собрали обычным webpack и вызываем render, скармливая initialState приложения.
Это позволило нам сделать SSR не переделывая специальным образом клиентскую часть (а вызовы window.something и подобные штуки у нас в рендер функциях и других подобных местах запрещены).
Мониторим сервер через okmeter: okmeter.io
Контейнеров node.js много и тут появляется задача: как выпускать обратно несовместимые релизы? Когда в питоне с новым релизом мы начинаем по-другому формировать json, отправлять другие поля, или не отправляем часть полей.
С одной стороны можно сохранить обратно-совместимые релизы и на такие изменения делать 2-3 релиза паровозиком (друг за другом). Но это неудобно.
Хочется это все делать за 1 релиз.
Тут на помощь приходит "хитрый" метод: у питонячьего приложения и node.js рендер сервера единая версия. То есть после каждого релиза версия и node.js приложения и питонячьего сервиса будет условно 2.0. А до релиза была, например 1.1.
Делим группу Node.js серверов на 2 и вначале катим релиз на половину машин.
Когда релиз раскатился, катим релиз на питонячье приложение.
И только затем раскатываем на оставшуюся часть машин.
Выглядит это вот так:

Остается посыпать решение инфраструктурой:
питон приложение идет в node.js с указанием версии. Node.js перед рендером сравнивает: а совпадает ли моя версия и версия питона? Если да, то просто рендерим и отдаем результат.
Если нет, отправляем 503. Питон получает 503 и пытается заретраить запрос на другой сервер. количество ретраев = количество инстансов / 2 + 1.
Можно пойти дальше и завести единое место с конфигом, которое будет балансировать и знать, где-какая версия раскачена, но мы пока до этого не дошли, это больше планы на будущее.