169 похожих чатов

Привет! Попалась небольшая статья про загрузку данных на уровне компонентов в

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

2 ответов

14 просмотров

Для лонгридов лучше юзать telegra.ph

Konstantin-Nosov Автор вопроса

Похожие вопросы

Обсуждают сегодня

Скажите, можно ли как-то "переместить" динамический массив из одной переменной в другую? Скажем, переместить из TList<> в TArray<>. Именно переместить, а не скопировать. Если ...
Eugene Krasnikov (ᴊɪɴ x)
37
Вот еще криповенькая штука. uMain.pas(517,3) Warning: Case statement does not handle all possible cases И ЧО? 😂
Александр (Rouse_) Багель
20
комрады, че-та лыжы не едут var tmpFont: TFont; begin tmpFont:= TFont.Create; try case rgFontColor.ItemIndex of 0: tmpFont.Color:= clWindowText; 1: tmpFo...
Ed Doc
34
Интересно, нет ли какого-то способа получить из dll не адрес самой метки, а адрес со смещением?
The Bird of Hermes
54
.model small .stack 100h .data a db 'Hello, World!', '$' ; исходная строка b db 20 dup(?) ; строка b с запасом на максимальную длину .code main: ...
Алексей -man
3
Или имеется ввиду именно что медленнее работа компилятора?
Куся 🌿⃤ __UKS
9
М-да. Почему бы просто со stringlist не работать?
Michael Longneck
23
вопрос, кого посмотреть в ютубе или где почитать про указатели чтобы раз и навсегда запомнить зачем они нужны и как правильно ими пользоваться? поделитесь хорошими ресурсами, ...
-
14
let sum = [1, 2, 3].map { String($0) }.flatMap { Int($0) }.reduce(0, +) let sum = [1, 2, 3].map { (num: Int) -> String in String(num) }.flatMap { (str: String) -> Int? in Int...
Yakov
7
Is there a digital way to cut the electricity from a usb in linux? It sounds weird, but it's exactly what I need to do. I tried to simulate the unplug/replug but is not the ...
Eduard Rivas
15
Карта сайта