6 небольших советов, чтобы подготовить NodeJS к высоким нагрузкам

Автор оригинала: Kobi Mantzur
  • Перевод

Сервис с распознаванием лиц «Look-A-Like» обслуживал тысячи пользователей одновременно

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


В рамках работы в MyHeritage мы разработали сервис doppelgänger для Евровидения 2019 года, с помощью которого, загрузив селфи, можно узнать на кого из участников конкурса вы похожи больше всего.


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


Весьма быстро мы поняли, что балансировщика нагрузки перед приложением, настроенного с помощью Auto Scaling, недостаточно для отказоустойчивости. Нам очень помогло следующее:


  1. Надейтесь на лучшее, но готовьтесь к худшему: измерьте, сколько одновременных пользователей получится обслуживать вашим приложением за время X (одним инстансом). Например, в нашем случае тестирование показало, что мы сможем обслуживать по 200 одновременных пользователей в каждом инстансе EC2 в течение 10 секунд, поэтому когда мы узнали, что должны обслуживать 10 000 одновременных пользователей, нам просто осталось подготовить 50 серверов за балансировщиком. Для проведения теста мы воспользовались отличным инструментом под названием JMeter.

    А этот туториал сильно помог при подготовке к выполнению замеров.
  2. Избегайте блокировок: блокирующие операции (вроде fs.readSync) заманчивы, потому что код выглядит чище, но они буквально убивают производительность. Вместо них используйте async/await операции, потому что во время выполнения асинхронной работы ЦП будет доступен и для других задач (см. Цикл событий).

    До: const res = fs.readSync('file.txt');
    После: const res = await fs.readAsync('file.txt');
  3. Увеличьте лимит памяти: Node по умолчанию настроен на ограничение в 1 ГБ. Если серверу доступно, скажем, 4 ГБ специально под ваше приложение, установить максимальный предел памяти вам придется вручную, используя CLI со следующим флагом: --max-old-space-size
    Пример: node --max-old-space-size=4096 server.js
  4. Убедитесь, что вы задействовали все ядра процессора: по умолчанию Node работает в одном треде. Если вы специально не настраивали конфигурацию, которая запускала бы несколько тредов, сэкономьте деньги, выбрав сервер с 1 ядром.
  5. Сократите количество обращений к приложению: настройте принудительный HTTPS и все редиректы как можно выше (например, на уровне прокси). Это позволит приложению не отвлекаться на лишнее, а, значит, быть доступнее для запросов, которые действительно важны.
  6. Обработка ошибок: используйте логирование, например Logz.io/AWS CloudWatch для отслеживания ошибок, которые могут привести к сбою приложения. НЕ СООБЩАЙТЕ обо всём подряд службам типа Slack, потому что сообщения обычно идут скопом и легко могут забить канал. Мы использовали отличную библиотеку под названием Winston для логирования в NodeJS.

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


Спасибо, что дочитали.

  • +20
  • 7,1k
  • 7
Поддержать автора
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 7

    +1
    В нашем случае эти советы привели к десятикратному улучшению производительности
    (¬‿¬ )

    А если по существу:
    Увеличьте лимит памяти
    А может лучше посмотреть, куда сжирается память?
    по умолчанию Node работает в одном треде
    Ага, а внутри в ноде нету своего пула потоков.

    P.S. для логирования возможно лучше использовать pino. Он вроде как даже быстрее будет.
      0
      Ага, а внутри в ноде нету своего пула потоков.

      Поправьте меня, если ошибаюсь, но эти потоки обрабатывают в фоне I/O, и заметного выигрыша увеличение количества ядер не даст. Дополнительные ядра могли бы пригодиться для вычислительных задач, но последние происходят в цикле событий, который как раз по-умолчанию однопоточный. Мне всегда казалось это как-то так работает.
        0

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

          +1
          Мой поинт в том, что многие почему-то думают, что если они поднимут инстансов сервиса по числу ядер, то оптимально утилизируют мощности (ведь нода в одном треде работает, да?), но при этом забывают, что эти же ядра используются и потоками из пула каждого поднятого инстанса ноды.
            0
            Можете перефразировать либо дать ссылку на литературу? Я все еще считаю что кластер из двух инстансов займут 2 ядра, даже если их больше. Или это не так?
              0
              Если посмотреть в top-е на запущенное в 1 воркер node приложение, можно увидеть кучку дополнительных потоков. Как минимум в отдельном потоке/потоках работает GC.
              Я, например, наблюдал максимум 120% нагрузку CPU от 1 инстанса node сервера.
        0
        Убедитесь, что вы задействовали все ядра процессора: по умолчанию Node работает в одном треде. Если вы специально не настраивали конфигурацию, которая запускала бы несколько тредов, сэкономьте деньги, выбрав сервер с 1 ядром.
        Вы о чём, о cluster? Или cli для запуска нескольких нитей (потоков)? Можно этот пункт расширить немного, пожалуйста?

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

        Самое читаемое