Node.js: карьерный обзор 2019 года

Микроисследование ИТ-специализированного кадрового агентства Spice IT Recruitment о текущей ситуации на рынке труда Node.js-разработчиков.

Мы в Spice IT уже десять лет занимаемся подбором ИТ-специалистов, поэтому знаем инсайты (и инсайдеров!) рынка каждой из профобластей.

Ведущий консультант Spice IT Юлия Артемова поговорила с тимлидами компаний, использующих Node.js в разработке, а Юлия Попова оформила результаты этих интервью в яркие иллюстрации.

Кроме того, в конце материала будет тест, (ладно, тут он тоже будет, вот он) с помощью которого можно спрогнозировать, за какое время у вас закроется вакансия разработчика вообще, и Node.js-разработчика в частности. Особенно полезен этот тест будет для ИТ-рекрутеров, а также для нанимающих менеджеров со стороны компаний.

По данным исследования Stackoverflow, Node.js, наряду с JavaScript, лидирует в рейтинге наиболее желанных и часто используемых технологий. А вот еще какие тренды мы выделили по результатам опросов наших респондентов.

Несмотря на то, что Node.js постоянно меняется, требования к разработчикам остаются — в общих чертах — неизменными.
Тимлиды как нанимающая сторона ждут от джуниоров базовых знаний JS, от мидлов — умения работать с фреймворками, а от синиоров — способности самостоятельно решать абстрактные задачи.

Медианные зарплаты начинаются от 50 тысяч рублей (для джуниоров) и достигают 250 тысяч рублей (для синиоров).

Востребованность разработчиков каждого из грейдов легко оценить по количеству офферов за две недели активного поиска.

Какие скиллы делают выше стоимость разработчиков на рынке труда:

  • RabbitMQ, Kafka.
  • Elastic Search.
  • Docker, Kubernetes.
  • Опыт с Highload.
  • Свободный английский.

Для мидлов зарплатная вилка при наличии вышеперечисленных навыков уверенно приблизится к 180 тысячам рублей. Для синиоров — к 250 тысячам рублей.

Прокачивать скиллы можно (и нужно!) с помощью постоянного самообразования. Вот несколько книг, горячо рекомендованных нашими респондентами к изучению.

Для джуниоров:

Для мидлов:

Из российских компаний Node.js в разработке используют (just to name a few): Rambler, «Яндекс», МТС, «Лаборатория Касперского», «ВКонтакте», EPAM, 2GIS, OneTwoTrip, «Сбербанк», Leroy Merlin, FxPro, Zecurion, LATOKEN, Waves, «Туту.ру», «Сравни.ру», Altarix, «Тинькофф», MERA, Profi.ru.

Из зарубежных (опять же just to name a few): PayPal, Netflix, Uber, LinkedIn, Ebay, Walmart, Medium, GoDaddy, Mozilla, Trello.

Если вы уже ищите или в скором времени планируете искать работу как Node.js-разработчик, наши респонденты советуют обратить внимание в первую очередь именно на эти компании.

В качестве бонуса (для тех, кто дочитал) мы составили несложный тест из 15 вопросов, честно ответив на которые, вы поймете, сколько времени у вас уйдет на закрытие вакансии разработчика.

0
132 комментария
Написать комментарий...
Anrey Myagkov

Сразу видно, что люди пишущие комменты про колбеки, ад в сложных проектах и прочую чушь, ничего кроме js на фронте в в проектах начала нулевых или по-свежее, но написанного Васей, пишущим на php и нежелающим вникнуть в область, не видели...

Ответить
Развернуть ветку
Влад Виолентий

абсолютная тривиальная задача в ноде сделать несколько запросов в базу данных mysql, postgres оборачивается еблей с колбеками и кучей пакетов в npm с реализацией коннектора к базе данных, и каждому блять надо написать свою реализацию коннектора

Ответить
Развернуть ветку
Alexander Perlamutrov

Пакет нужен ровно один - либа для работы с базой. Где здесь хотя бы один колбек?

```

const conn = Pool.connect('DSN');

const user = await conn.query({

  text: 'SELECT * FROM users WHERE id=$1',

  values: [1],

  name: 'select_user'  // Prepared statement's name

});

```

И тут, внимание, киллер-фича - параллельное выполнение запросов к базе:

```

const [users, products] = await Promise.all([

  conn1.query(),

  conn2.query()

]);

```

И если в питоне так сделать можно при помощи дополнительных библиотек, то в PHP вообще не уверен.

Ответить
Развернуть ветку
Alex

Сомнительная фича, конечно. Тут только одно преимущество: если клиент использует пул подключений, то запросы уйдут по разным соединениям параллельно.

Но тут именно преимущество самого ивент лупа, а не клиента БД.

Ответить
Развернуть ветку
Alexander Perlamutrov

Да, это преимущество Node.js в целом. Фича очень крутая. Только подумайте, у вас один процесс на PHP может обрабатывать только один клиентский запрос одновременно, а один процесс на Ноде - 1000 клиентских запросов, если основное время при генерации ответа уходит на ожидание ответов от базы или внешних API.

Ответить
Развернуть ветку
Влад Виолентий

php-fpm, не?

Ответить
Развернуть ветку
Alexander Perlamutrov

Вот, я нашёл либу для PHP с неблокирующей сетью: https://reactphp.org/

Используя её, можно сделать что-то похожее на Node.js.

Ответить
Развернуть ветку
Michael Smith

ReactPHP уже 7 лет есть.

Только таких задач очень мало, где реально требуется ассинхронный фреймворк. Инкапусуляция одного запроса на один процесс это не недостаток, а способ параллелизма без головной боли и без необходимости вообще думать про паралельное выполнение.

Ответить
Развернуть ветку
Alexander Perlamutrov

Вы серьёзно?! Любой веб-сервис, который использует базу данных и внешние API, и при этом имеет большее количество одновременных пользователей, чем ядер CPU - это идеальный случай применения для платформ с неблокирующим IO.

Ответить
Развернуть ветку
Michael Smith

Несогласен, но холивар разводить не хочу.

Реально где это нужно, когда есть по 200 запросов к внешним API, как у автора вот тут https://habr.com/ru/post/278755/

Таких кейсов один на 1000

Ответить
Развернуть ветку
Alexander Perlamutrov

А запросы к серверу баз данных вы не считаете? В ваших сервисах вы большую часть времени ответа делаете вычисления или ждёте ответа от базы данных?

Ответить
Развернуть ветку
Michael Smith

Чтобы не ждать ответа от БД делают кэш в памяти, а не усложняют архитектуру без необходимости.

Также процесс в простое много ресурсов не потребляет.

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

Ответить
Развернуть ветку
Alexander Perlamutrov

Прелесть Node.js как раз в том, что не нужно усложнять архитектуру, не нужно изучать дополнительные фреймворки и библиотеки, разаработчики Node.js уже всё сделали за вас. Вы можете писать в "синхронном" стиле, просто добавляя слово await перед сетевыми запросами. Всё.

Ответить
Развернуть ветку
Michael Smith

Что оверхед архитектуры скрыт от вас фреймворком, это еще не значит что его нету.

С одноразовыми PHP процессами не требуется думать об утечках памяти и о других прелестях асинхронности.

Ответить
Развернуть ветку
Alexander Perlamutrov

Процесс в простое, конечно же, не занимает ресурсов, но и не обрабатывает входящие запросы. В итоге на выходе вы получаете загруженный на 1% многопроцессорный сервер, который может обработать параллельных запросов не больше, чем у него ядер. Запросы накапливаются в очереди, время ответа растёт, часть запросов начинает отваливаться по таймауту.

Кэшом можно решить очень ограниченный спект задач, когда у вас данные меняются очень редко и строго централизованнно. В противном случае, инвалидация кэша усложнит вашу архитектуру гораздо сильнее.

Ответить
Развернуть ветку
Michael Smith
В итоге на выходе вы получаете загруженный на 1% многопроцессорный сервер, который может обработать параллельных запросов не больше, чем у него ядер.

Эм, что? Откуда вы это взяли?

Ответить
Развернуть ветку
Alexander Perlamutrov

Ну смотрите, у вас есть, например, PHP-FPM с ограниченным max_children, вы его выставили, например, в 8 по количеству ядер процессора (больше ставить опасно, если не понимаете почему, могу объяснить отдельно). К вам пришли 100 одновременных запросов. В рамках обработки каждого запроса в отправляете запрос к серверу баз данных, который выполняется, например, 100мс. Получается, что 8 процессов-воркеров ждут 100мс, обрабатывая 8 входящих запросов, а остальные 92 попадают в очередь. Все эти 100мс у вас загрузка сервера 0%. Если предположить, что парсинг запроса и сериализация ответа занимают 1мс, то получается, что у вас процессор реально был загружен только 1мс из 100, то есть ~1%.

Ответить
Развернуть ветку
Michael Smith
Ну смотрите, у вас есть, например, PHP-FPM с ограниченным max_children, вы его выставили, например, в 8 по количеству ядер процессора

https://serversforhackers.com/c/php-fpm-process-management

Now we can decide how many processes the server is allowed to spin up.
Here's a generic formula:
Total Max Processes = (Total Ram - (Used Ram + Buffer)) / (Memory per php process)
This server is has about 3.5gb of ram. Let's say PHP uses about 30mb of ram per request.
We can start with: (1024*3.5) / 30 = 119.46. Let's just go with 100 max servers.
Ответить
Развернуть ветку
Michael Smith

И вот тоже

https://thisinterestsme.com/php-fpm-settings/

For pm.max_spare_servers, multiply the number of cores on your server by 4.

На современных серверах с 16-32 ядрами и парой процессоров это получится 256 одновременных запросов.

Хватит?

Ответить
Развернуть ветку
Alexander Perlamutrov

Вот это уже больше похоже на правду. Но если вы хотите обрабатывать >1k RPS с _одного_ ядра, то не хватит.

Просто если задрать количество процессов, ориентируясь на объём памяти, как предлагается  в первой статье, то эти процессы под нагрузкой начнут "драться" за процессорное время, будет огромное количество переключений контекста, и в итоге производительность процессора деградирует https://ru.wikipedia.org/wiki/%D0%9F%D0%B5%D1%80%D0%B5%D0%BA%D0%BB%D1%8E%D1%87%D0%B5%D0%BD%D0%B8%D0%B5_%D0%BA%D0%BE%D0%BD%D1%82%D0%B5%D0%BA%D1%81%D1%82%D0%B0

Ответить
Развернуть ветку
Alexander Perlamutrov

Статья описывает старое доброе распараллеливание обработки через форк процессов. Это то, как работает PHP-FPM. Использование неблокирующего IO - это немного другая парадигма. Физически вы по-прежнему остаётесь однопоточным, никаких параллельных действий не осуществляется. Просто все операции с сетью или файловой системой перестают быть блокирующими, позволяя вам вместо ожидания занимать CPU полезной работой. То есть это просто более качественный способ утилизации процессорного времени ваших серверов.

Ответить
Развернуть ветку
Michael Smith
Статья описывает старое доброе распараллеливание обработки через форк процессов

И? Нету никакого ограничения, что число параллельных запросов не может быть больше числа ядер, как вы утверждали.

То есть это просто более качественный способ утилизации процессорного времени ваших серверов.

Я про это свою позицию уже написал выше.

Железо недорогое, мне проще добавить его чем искать утечки памяти.

Нет смысла усложнять и вводить лишние парадигмы, где это не требуется задачами.

Ответить
Развернуть ветку
Alexander Perlamutrov
 И? Нету никакого ограничения, что число параллельных запросов не может быть больше числа ядер, как вы утверждали.

В однопоточных блокирующих системах количество параллельно обрабатываемых запросов равно количеству процессов-воркеров. Если вы наплодите этих процессов сильно больше, чем у вас ядер на сервере, то он приляжет под нагрузкой, потому что процессы будут "драться" за процессор. Грубо говоря, сервер попытается одновременно обработать больше запросов, чем он физически способен. Вместо того, чтобы выстроить их в очередь перед сервером и попробовать обработать частями. Поэтому ограничение на количество воркеров нужно. И подбирать его нужно под вашу конкретную нагрузку, а не исходя из чьих-то формул. Например, для сервисов, которые занимаются только вычислениями, типа обработки видео, для лучшей производительности нужно иметь строго 1 процесс на ядро. Если же у вас веб-сервис, который ходит в бд или внешние api, то можно приподнять количество процессов выше количества ядер. Но это вам придётся всегда делать вручную. Мониторить нагрузку, править конфиги. Системы с неблокирующим IO получаются практически самобалансируемыми. Единственное, что для них нужно мониторить - это потребление CPU, если оно приближается к 100%, то нужно срочно добавлять новый сервер в ваш кластер.

Ответить
Развернуть ветку
Michael Smith
то он приляжет под нагрузкой, потому что процессы будут "драться" за процессор. Грубо говоря, сервер попытается одновременно обработать больше запросов, чем он физически способен

У вас постоянно крайности, с одним процессом на ядро у вас сервер занят на 1%, а если больше, то сразу приляжет под нагрузкой ))

С чего процессам драться, если, по вашим словам, они тупо простаивают и ждут ответа от БД?

Никто не делает один процесс на одно ядро, вы какие-то сказки рассказываете.

Все сайты как-то работают и не испытывают с этим проблем.

Ответить
Развернуть ветку
Alexander Perlamutrov
 Вы уж определитесь, то у вас сервер занят на 1%, то он приляжет под нагрузкой ))

Если вы сделаете 1 ядро -> 1 воркер, то будет загружен на 1% на сервисах с хождением в бд и внешние апи. Если сделаете 1 ядро -> 100 воркеров, то приляжет. Хорошее количество воркеров где-то посередине, но вам нужно его найти экспериментально на вашей кодовой базе. И оно может меняться вместе с профилем нагрузки.

Ответить
Развернуть ветку
Michael Smith

Ну вот, оказывается не один процесс на ядро )

Я уже выше написал, 4 процесса на ядро это полностью безопасно (там за памятью больше надо смотреть но и с этим на сервере нормально), и это уже дает лимит в сотни параллельных процессов на современном сервере.

Проблемы никакой нет, как и нет необходимости использовать асинхронный фреймворк в 99% серверных приложений.

Ответить
Развернуть ветку
Alexander Perlamutrov

4 для веб-сервиса - это безопасно, но не оптимально. Хотя, например, если у вас там есть эндпоинт для загрузки и кропа аватарок, то уже не так безопасно. Если у вас есть монолитное веб-приложение, где один запрос обрабатывается 1мс, а другой - 10с (например, генерация какого-нибудь дикого отчёта в админке с кучей запросов в бд) и блокирует воркер целиком, то платформа с неблокирующим IO очень подходит. И это точно не 1% серверных систем.

Ответить
Развернуть ветку
Michael Smith

С обработкой изображений постоянный процесс быстрее сдохнет из-за утечек памяти.

Для долгих ответов API кэширование решает.

Асинхронность не нужна, кроме ускоспециализированных случаев где нужно тысячи соединений держать, типа чатов итп.

Ответить
Развернуть ветку
Alexander Perlamutrov

Нет. PHP-FPM - это менеджер пула процессов. Но каждый процесс в пуле обрабатывает одновременно всего один запрос. Таким образом, если у вас сервер с 8 ядрами, вы можете поднять 8 процессов-воркеров, каждый из которых будет обрабатывать по одному запросу одновременно. Получается, что пропускная способность вашего сервиса - 8 конкурентных запросов. Например, если сервис ходит несколько раз в базу данных или какой-нибудь внешний API, и отвечает за 200мс, то вы сможете выжать из него 40 RPS.

Node.js базируется на эвент-лупе с неблокирующим IO. Все сетевые, файловые операции - неблокирующие. Поэтому даже в рамках одного процесса после того как он отправит запрос в базу, он не будет тупо ждать ответа как в PHP, а продолжит обрабатывать другие входящие запросы. То есть один процесс-воркер сможет обрабатывать от 1 (если сервис занимается CPU-вычислениями, никуда не ходит) до 1000 (если сервис ходит в базу и внешние API, а из вычислений только парсинг запроса и сериализация ответа) одновременных запросов. Таким образом в теории вы можете получить до 40000 RPS с 8-ядерного сервера.

Ответить
Развернуть ветку
Влад Виолентий

Если API отвечает 200 мс - значит его надо кешировать.

и как отметили в комментарии - есть reactphp, swoole

Ответить
Развернуть ветку
Alexander Perlamutrov

Хорошо, не 200, а 5. Разница в подходах останется прежней.

Ответить
Развернуть ветку
Alexander Perlamutrov

Ну и как, много проектов вы сделали на reactphp или swoole? А прелесть Node.js в том, что даже начинающий разработчик сделает свой сервис более производительным "из коробки". И для этого не нужно осваивать дополнительные фреймворки/библиотеки или изучать синтаксис го-рутин. Вот и вся разница.

Ответить
Развернуть ветку
129 комментариев
Раскрывать всегда