All streams
Search
Write a publication
Pull to refresh

Comments 13

Часто в SSR спасает кэширование. Можно кешировать SSR результат допустим для неавторизированных пользователей. Для них вариативность ответа часто мала. Плюс можно ключ кеша сформировать для разных групп пользователей.

Но вообще, renderToString даже базовый, ужасно медленный, там легко рендер будет выполняться 20-40мс. Плюс ещё много чего лишнего выполняется на сервере, что нужно только на клиенте, типа чтения пропсов событий, создание массива зависимостей для хуков. Ещё, например, makeObservable от mobx совершенно не нужен на клиенте, но создаёт лишние вычисления. Поэтому нужно брать профайлер в зубы и смотреть, может там какая-то функция формирования урлов ссылок или картинок кучу вычислений делает.

Ещё в идеале нужен инструмент, который бы позволял настраивать кеш верстки для отдельных компонентов. Условная шапка или футер не особо вариативны, поэтому можно высчитать строку один раз (такую библиотеку видел, но она не особо развивается).

Но в целом, конкатенация строк в js не особо быстрая, даже через шаблонизатор не сильно шустрее будет работать. Все хочу попробовать сделать нативный модуль renderToString на С или Rust, но пока руки не доходили.

Плюс я так и не понял, у автора NodeJS отдает статику js/css? Это должен делать nginx или аналоги. Плюс судя по проблемам с сетью, у него не настроен gzip, но это мои предположения.

Можно кешировать SSR результат допустим для неавторизированных пользователей

А для авторизованных он как бы вообще не нужен, от слова совсем. Для авторизованных просто режим SPA (Client Side Rendering). Более того и Next.js вообще нафиг не нужен для SSR. Это задача решается элементарно, один раз в жизни пишется сервис который на вход получает набор урлов потом будет puppeteer ходить и класть результат в кэш, а при запросах отдавать ответ напрямую из кэша. Никаких ограничений и костылей next.js'a, гигантская производительность ибо все отдается из кэша и одна виртуалка за 5$ обслужит много тысяч RPS.

Если у вас статические данные, а если это биржа, обменник , аукцион??

Если у вас статические данные

Ну обычно раз в 5 минут и так в фоне все страницы обновляются, поэтому они "устаревают" в среднем на 5 минут при самом простом исполнении. Но если нужно реагировать быстро, то опять же не проблема, webhook/rabbitmq и т.п. нотификация о том, что надо обновить кэш для такого-то урла и тогда задержка условно в пару секунд, что для 99.99999% случаем более чем достаточно

если это биржа, обменник , аукцион??

Так при открытии страницы на блоках с катировками лоадер "инитного получения данных", а дальше работает как обычно websocket и т.п. и обновляется в real time.

Плюс я так и не понял, у автора NodeJS отдает статику js/css?

Ну если брать чисто next.js то он даже картинки кеширует на беке, оптимизируя разрешение и прочее, как вам нужно и полностью сервит статику. Если у вас изоморфное app, то nginx обычно в роли балансера. Это уже нужно предметно по задаче обсуждать. Понятное дело статья про сыерического коня в вакууме.

По поводу отдельных компонентов, это уже микрофронты.

Сейчас еще мода на htmx, alpine и старые добрые шаблоны без js рендера на беке. Там и cnd кешить может угу.

Фронтовые стеки сейчас дикий зоопарк, как мем с синим квадратом

Ради интереса запустил ab тест на машине 2 ядра 4 гига, чистое node.js + express приложение и чистый nginx. Все уперлось в возможности машины для запуска ab. Результат ~2000 RPS. Есть у кого возможность запустить нормально на двух машинах?

ab -c 100 -n 50000 http://189.12.101.46/ -- для node.

Concurrency Level: 100 Time taken for tests: 24.093 seconds Complete requests: 50000 Failed requests: 0 Total transferred: 8850000 bytes HTML transferred: 100000 bytes Requests per second: 2075.30 [#/sec] (mean) Time per request: 48.186 [ms] (mean) Time per request: 0.482 [ms] (mean, across all concurrent requests) Transfer rate: 358.72 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 1 23 168.3 1 8112 Processing: 1 25 17.0 27 847 Waiting: 1 25 15.4 27 824 Total: 2 48 167.7 28 8136 Percentage of the requests served within a certain time (ms) 50% 28 66% 33 75% 35 80% 36 90% 40 95% 53 98% 233 99% 1030 100% 8136 (longest request)

ab -c 100 -n 50000 http://189.12.101.46/ -- для nginx.

Concurrency Level: 100 Time taken for tests: 24.914 seconds Complete requests: 50000 Failed requests: 0 Total transferred: 7900000 bytes HTML transferred: 100000 bytes Requests per second: 2006.92 [#/sec] (mean) Time per request: 49.828 [ms] (mean) Time per request: 0.498 [ms] (mean, across all concurrent requests) Transfer rate: 309.66 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 1 37 317.6 2 9740 Processing: 1 7 77.7 2 7522 Waiting: 1 7 76.3 2 7522 Total: 2 44 340.1 3 9839 Percentage of the requests served within a certain time (ms) 50% 3 66% 4 75% 4 80% 4 90% 6 95% 7 98% 1007 99% 1031 100% 9839 (longest request)

Nginx

server { listen 80; server_name localhost; location / { return 200 "OK"; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } }

server.js

const express = require('express') const app = express() app.disable('x-powered-by') app.get('/', (req, res) => res.send('ok')) app.listen(80)

Я посмотрел на результаты исследований и ничего не понял. Как у Вас получилось получить 34 RPS для 20кб файла?

Я, может быть, в другой вселенной живу, но я не понимаю, как можно так затормозить, например kestrel, чтобы на VPS было 34RPS. Это дикость какая-то.

Без понятия что такое kestrel, гугл говорит что это что-то типо сервера для .net. Но 34 rps это про ssr, там по сути подымается движок браузера в нем из кода генерится html, отправляется клиенту и инжектится прямо строкой, ну или типа того. Не совсем тоже самое что просто отдать 20кб файл. А если взглянуть на сайт автора то и непонятно нахрена там вообще реакт.

там по сути подымается движок браузера

Не, там поднимается V8, среди сред интерпретируемых языков он очень шустр. Просто ему нужно выполнить все дерево компонентов, получить VDOM дерево реакта и снова по нему рекурсивной функцией пройтись и получить html. Я не понимаю почему, но renderToString написан ужасно не оптимально. Можно было сделать хитрее и сразу html составлять по вызову createElement, вместо чем потом снова рекурсивно обходить дерево.

На клиент, если это SSR классический, идёт потоковый html, который в нескольких event loop вставляется на страницу, это гораздо быстрее и эффективнее, чем составлять DOM дерево на клиенте или делать innerHTML. Идея очень крутая, но реализация хромает.

Исчерпывающее описание 99% JS статей про производительность.

Это криворукость, а не дикость:
1. Встроенный сервер NodeJS - убогое Г (решается аддоном типа uWS или переходом на среду исполнения Bun, должно на мелких ответах ~1-4KiB дать увеличение RPS с 30k+ до 90k+ на 1 ядро. идеал под JS - v8 и указатели + системные константы + системные вызовы, выкинутые в JS [обернуть в производительные JS абстракции] - так ещё в 1.5 раза можно поднять производительность и будет есть меньше проца и памяти)
2. express + React SSR + Next.js - убогое ГГГ (использовать что-то поверх uWS в NodeJS + шаблоны формата типа Marko [лучше не сам marko, т.к. тоже больше Г, чем не Г] и пр. решения не для ленивых)
3. Использование JS строк (rope string => flatten => utf8 encode - зачем? из БД бинарные данные, статичные строки можно заранее перегнать и потом соединять бинарники. если кол-во кусков очень большое, то строки могут быть быстрее за счёт батча операций, но здесь ещё GC даст циклу событий неизвестно когда стать куском Г, а в задержках 0+0>0, т.е. степень Г будет быстро нарастать при близкой к максимуму нагрузке)
4. VPS (тоже больше Г, бывает запуск докера внутри VPS делает ситуацию лучше, но это зависит от фазы луны)

В статье: берём 6Г и видим 34RPS, возмущаемся, оставляем 3Г и радуемся (если бы оставил VPS и убрал остальные 4Г, кроме JS строк, то даже дедик не нужен, т.к. оптимизированный вариант под полную утилизацию канала 1Гбит+).

Мне всё ещё любопытно, насколько дальше можно разогнать сервер — без Next.js

Ну вы же понимаете js использует commonjs который по сути реализован за счёт движка написанном на C++. Если хочется экстрима, вспомните как PHP расширяют сишными либами.

Так же стоит рассмотреть bun, deno

И конечно в реале, есть cnd кеширование, балансеры и так далее)

Скорее вы возьмете другой стек под специфику, чем упретесь в производительность ноды.

Классические текстовые шаблоны имеют крайне ограниченный контекст, поэтому они зачастую преобразуются в нативный код языка, в котором они обрабатываются, с последующим скармливанием в jit. А у реакт компонентов, помимо шаблона, есть еще куча вещей, которые надо обработать - стейт, хуки, события, виртуальный дом. Нас же интересует гидрация, с последующим продолжением работы на клиенте, поэтому - извините. Это всё несказанно медленнее классических текстовых шаблонов. С другой стороны, идея ssr как раз таки расчитывает на гидрацию, после которой рендерить компоненты клиентская сторона, разгружая сервер, н, как это принято в реакт сообществе - что-то пршло не так и гачали вводить серверные компоненты, которые противоречат изначальной идее ssr (было бы логичнее делать серверный рендер только для поисковиков, а для человека - просто отдать скрипты приложения). Ну и в целом, реакт и некст жс очень тяжелые, есть смысл попробовать ssr на vue, svelte, solidjs - там всё гораздо быстрее и проще. А цифры в статье - это курам на смех, попахивает пхп и 2000 годом :)

Не понимаю, зачем так усложнять. Почему нельзя развернуть сервис на ванильной js, сделать load balancer и прикрепить к autoscaling group на aws? Будет эффективнее vds.

Sign up to leave a comment.

Articles