🔥

Тред (@xnimorz)


Тут мы подходим к важной теме 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 запроса. Схематично это выглядит вот так:
notion image

На сервере крутится 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 и вначале катим релиз на половину машин. Когда релиз раскатился, катим релиз на питонячье приложение. И только затем раскатываем на оставшуюся часть машин. Выглядит это вот так:
notion image

Остается посыпать решение инфраструктурой: питон приложение идет в node.js с указанием версии. Node.js перед рендером сравнивает: а совпадает ли моя версия и версия питона? Если да, то просто рендерим и отдаем результат.

Если нет, отправляем 503. Питон получает 503 и пытается заретраить запрос на другой сервер. количество ретраев = количество инстансов / 2 + 1.

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