NextJS приложениях - https://medium.com/@A__G__B/component-level-data-fetching-in-next-js-with-srr-8d35cdc5849e
Загрузка данных на уровне компонентов обеспечивается с помощью либы https://github.com/kmoskwiak/useSSE
Оказалось, что use-sse использует подход double render (если я не ошибаюсь, его использует Appolo).
Первый рендеринг приложения на сервере позволяет собрать промисы с загрузкой данных для каждого компонента, второй рендеринг выполняется после резолва этих промисов.
Меня всегда сильно смущал такой подход - дело в том, что ReactDOMServer.renderToString и даже ReactDOMServer.renderToNodeStream работают синхронно, и соответственно блокируют event loop.
На гитхабе мне попадались заброшенные попытки форкнуть ReactDOMServer и сделать рендеринг асинхронным, выполнять его по кусочкам, как раз для предотвращения долгой блокировки event loop.
Почему это важно?
React работает быстро на сервере, но для большого дерева компонентов, renderToString может занимать условно 100ms (на примере некоторых наших приложений от 30 до 300 при больших нагрузках).
Скорее всего, это будет самая длинная синхронная операция на вашем сервере.
Фактически, эта операция - самый главный ограничитель вашего RPS на одну ноду с SSR приложением.
Если я правильно понимаю как работает нода, когда приложение получает одновременно десять запросов, с синхронными операциями по 100ms, одинадцатый запрос получит ответ минимум через секунду, т.е. они буквально станут в очередь (это все без учёта прочих асинхронных действий на сервере).
Каждый новый запрос будет ухудшать ситуацию, увеличивать время ответа следующего запроса, будет заметно больше лаг event loop.
С такими метриками, при SSR с React можно рассчитывать что одна нода потянет 10 RPS, что достаточно не серьезные нагрузки с точки зрения high load, но пока что это наша фронтовая реальность.
С учётом этих факторов, double rendering кажется чем-то невероятно дорогим.
И в будущем Server Components будут решать проблему загрузки данных на уровне компонентов.
Я очень рассчитываю на React 18 и новую архитектуру работы на сервере.
Новые механизмы для рендеринга на сервере, вместе с Suspense, больше не будут рендерить приложение в один синхронный проход, эта задача будет разделена на отдельные асинхронные задачи (одна задача на один юнит - реакт компонент), аналогично concurrent rendering на клиенте.
Если node.js вместо десятка синхронных задач по 100ms будет получать тысячу задачек по 1ms, это в разы увеличит количество запросов, которые нода может обрабатывать одновременно без существенного влияния перформанс, т.е. новые запросы будут гораздо меньше влиять на время ответа для последующих запросов.
Например, все запросы, которые будут возвращать ошибки 500 или 404, или редиректы, не будут ждать синхронную очередь, а смогут вклиниться между остальными запросами.
Из минусов при асинхронном рендеринге - увеличение RPS будет примерно одинаково по чуть-чуть замедлять ответы на все обрабатываемые запросы, в случае с синхронными рендерингом первый запрос в очереди всегда будет обработан максимально быстро.
И возможно в новом прекрасном мире SSR легче будет делать high load :)
Отдельно хочу вспомнить про кэширование.
Конечно, самое эффективное ускорение рендеринга на сервере - это его отсутствие, и в ряде случаев можно кэшировать результаты рендеринга, или даже использовать SSG.
Но в приложениях с большим количеством персонализации, скорее всего кэшировать будет просто нечего, т.к. каждый пользователь будет получать уникальный HTML.
В tramvai приложениях на tinkoff.ru мы активно кэшируем на сервере только ответы на запросы, которые не требуют персонализации.
В конце хочу порекламировать свой обзор дискуссий reactwg/react-18, в том числе там есть разбор новой архитектуры на сервере - https://superoleg39.notion.site/reactwg-react-18-3914d12cc91e430b974495bffea86472
Для лонгридов лучше юзать telegra.ph
Это автору можно сказать
Обсуждают сегодня