Про библиотеки, дизайн кода, паттерны, стабильность, дебагинг, работоспособность std либы и так далее, я уже писал и буду писать, сейчас я хочу рассказать о конкретных проблемах, которые уже самой Node.js очень тяжело решить
Application footprint
Приложение на Node.js спокойно съедает 250-500 мб и выше оперативы (и это просто на само приложение, без каких-либо доп аллокаций), поднимается до нескольких минут и также медленно опускается.
Главное преимущество распределенной архитектуры – возможность горизонтального масштабирования, когда мы можем говорить о сотнях инстансов сервисов, но 100 инстансов на Node.js это 30-50 Gb RAM.
Да, несомненно, железо сейчас дешевое, но блять, такое же приложение на Go, будет жрать 10-50 mb оперативы, а значит 100 инстансов это 5 Gb RAM и это всего парочка супердешевых тачек.
Deploy time
Когда у вас 1 монолит или 3-4 сервиса по 2-3 инстанса вы не заметите сколько времени займет деплой новой версии приложения.
Но когда у вас 20+ сервисов по 5-7 инстансов каждого из них, тут время старта системы может оооочень сильно растянуться.
Бывает, что приходится ждать целый час пока они поднимуться, почему? Потому куберу надо скачать новый гигабайтовых образ и выделить тачку с достаточным RAM + время старта Node.js приложения очень долгое.
(отсутпление: да, мы используем partial deploy, когда катяться только изменившиеся сервисы, но когда было изменение shared lib это начинает касаться всех, а такое часто происходит)
И ты весь этот битый час сидишь и ждешь, чтобы потом проверить метрики и пойти заниматься своими задачами.
Go стартанет практически моментально при этом можно всегда держать пару пустых легких тачек, чтобы новые сервисы падали на них, а тачки, на которых сейчас приложения как раз таки оставаались пустыми для следующего деплоя.
Event Loop
Перегрузить Node.js процессы очень просто, причем без всяких сложных операций в CPU или Memory. На 800 коннектов от RMQ мы спокойно наблюдаем event loop lag в 250 мс, а это просто жопа.
(отступление: это может быть лично нашей проблемой с RMQ, amqplib или еще чем-то, но мы пользуемся прям самыми стандартными методами, на каждой отдельной очередь 1 RPS и внутри бизнес-логики только 10 IO вызовов)
А представьте что у вас 5000-10000 устройств (и это только начало) и у каждого по очереди, теперь посчитайте что да как.
Go, на моей практике, в один инстанс держал десятки тысячь коннектов и никто ниочем не задываллся (а на самом деле он может и сотни тысячь)
CI/CD
Время сборки образа и его размер измеряется десятками минут и гигабайтами веса, прохождения jest тестов может занимать до 6 Гб памяти (и это просто их issue) и почти час времени (ну будет у runner столько ресурсов, чтобы тесты всех сервисов запустить в параллель)
Встроенный в Go тесты делают тоже самое в мнгновение ока.
Тут может быть еще куча других пунктов, но вот это прям база, когда мы без вкусовщины упираемся в предел Node.js и для того, чтобы выйти за него придется потратить намного больше сил, чем например начать писать новые и аккуратно рефаторить старые сервисы, используя Go (ну или вообще с него начинать)
P.S.
Случайно наткнулся на
видео как раз об этом, но в сравнении с Java.
Первый тест четко показывает картину, которую можно наблюдать при сравнении Go с Java / C# / Node.js
А во втором тесте он удивляется почему вдруг Go начал взлетать по CPU, но просто потому что допустил суперпростую ошибку (видимо, он раньше не писал на Go), которую я нашел просто посмотрев на исполняющуюся функцию и ее исправление – это добавление ровно 1 строчки кода.