Секреты создания производительных веб-приложений на

Секреты создания производительных веб-приложений на

Наличие интуитивно понятной файловой структуры играет огромную роль – легче добавлять новый функционал и рефакторить код. Подходящий способ структурирования выглядит так:

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

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

Чтобы обеспечить соблюдение правил в команде, вы можете настроить линтер, который сообщит, когда вы переходите через установленный лимит строк в файле или если одна строка имеет длину более 100 символов.

Секреты создания производительных веб-приложений на

1.1. Переменная окружения NODE_ENV

Установив переменную окружения NODE_ENV , вы примерно трёхкратно увеличите производительность. В терминале это можно сделать следующим образом:

Если вы используйте server.js , добавьте следующее:

1.2. Включаем Gzip-сжатие

Установите npm-пакет компрессии:

Затем добавьте следующий фрагмент в свой код:

1.3. Асинхронные функции

Не блокируйте поток выполнения и не используйте синхронные функции. Вместо этого используйте функции Promises и Async/Await. Если у вас есть доступ только к синхронным функциям, оберните их в async-функцию, которая будет выполняться вне основного потока.

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

1.4. Система логов

Чтобы унифицировать журналы по всему Express.js-приложению, а не использовать console.log() , используйте агент для централизованного ведения, структурирования и сбора журналов.

Можно использовать любой инструмент управления журналами, например, Sematext, Logz.io или Datadog. Практически все агенты базируются на Winston и Morgan. Они отслеживают трафик запросов API с помощью промежуточного программного обеспечения. Это сразу же даст вам журналы и данные по каждому параметру, что важно для отслеживания производительности.

Вот так добавляется логгер и промежуточное ПО:

Превью того, что получается на выходеПревью того, что получается на выходе

1.5. Обработка ошибок и исключений

При использовании в коде Async/Await для обработки ошибок и исключений рекомендуется применять операторы try-catch , а также использовать для ведения журнала ошибок Express logger.

Кроме того, рекомендуется настроить catch-all error :

Здесь будет поймана любая ошибка, выброшенная в контроллере. А ещё можно добавить слушателей в сам процесс:

1.6. Следите за утечками памяти

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

Заметить утечку гораздо проще, чем вы думаете. Если память процесса продолжает неуклонно расти, а не периодически сокращается в результате сборки мусора – скорее всего это она и есть. В идеале вы должны сосредоточиться на предотвращении утечек, а не на их устранении и отладке.

Добавьте в Express-приложение сборщик метрик, который будет хранить их в одном месте. Это поможет проанализировать данные и добраться до основной причины.

Результаты работы сборщика метрикРезультаты работы сборщика метрик

В чем прелесть – это всего лишь одна строка кода. Добавьте ее в файлик app.js .

Благодаря этому вы получите доступ к нескольким информационным панелям, дающим ключевое представление о том, что происходит с вашим Express-приложением. Данные можно фильтровать и группировать для визуализации процессов, памяти, использования CPU и HTTP-запросов/ответов. Что вы должны сделать сразу же – настроить оповещения об изменениях в работе софта.

Двигаемся дальше от Express.js к конкретным советам и рекомендациям по JavaScript и о том, как его использовать оптимизированным и надежным способом.

Секреты создания производительных веб-приложений на

2.1. Чистые функции

Чистые функции – это функции, которые не изменяют внешнее состояние. Они принимают параметры, что-то делают с ними и возвращают значение.

Вместо использования var , применяйте только const и полагайтесь на чистые функции для создания новых объектов вместо изменения существующих. Это связано с использованием функций высокого порядка в JavaScript, например .map() , .reduce() , .filter() и т. д.

2.2. Параметры объекта

JavaScript – слабо типизированный язык. В вызов функции может быть передан один или несколько параметров. Даже если объявление функции имеет фиксированное число определенных аргументов. Этот огрех можно решить, используя объекты в качестве параметров функции.

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

2.3. Тестирование

Используйте что-нибудь простое, например, Mocha и Chai. Mocha – это фреймворк для тестирования, а Chai – assertion библиотека.

Установите npm пакеты:

Давайте потестим функцию. В test.js добавьте следующее:

Добавьте это в package.json :

Теперь запустим тесты, выполнив следующую команду в терминале:

Выведется примерно такое:

3.1. Управление переменными среды в Node.js с dotenv

Dotenv – это модуль npm, позволяющий загружать переменные среды в любое Node.js приложение. В корне вашего проекта создайте .env файл. Здесь вы добавите все необходимые переменные окружения.

Загрузка файл проста. В верхней части app.js разместите dotenv:

Dotenv по умолчанию загружает файл с именем .env . При необходимости прочитайте руководство по настройке нескольких dotenv-файлов.

3.2. Перезапуск приложения с помощью Systemd

Systemd – часть строительных блоков ОС Linux. Он запускает и управляет системными процессами. Вам нужно запустить Node.js процесс, как системную службу, чтобы он восстанавливался после сбоев.

На виртуальной машине или сервере создайте новый файл в разделе /lib/systemd/system/ :

Две важные строки в этом файле – ExecStart и Restart. ExecStart запустит ваш server.js с помощью бинарника /usr/bin/node (обязательно проверяйте абсолютный путь к файлу server.js ). Функция Restart=on-failure перезапустит приложение, если оно «обвалится».

После сохранения fooapp.service , перезагрузите демона и запустите скрипт.

3.3. Перезапуск приложения с помощью PM2

PM2 существует уже несколько лет. Эти ребята используют специальный кастомный скрипт, управляющий и запускающий server.js . Он проще в настройке, но обременён другим Node.js- процессом, выступающим в качестве Master-процесса и менеджера для вашей Express.js.

Сначала нужно установить PM2:

Затем запустите приложение, выполнив следующую команду в корневом каталоге Express-проекта:

Флаг -I max гарантирует, что приложение будет запущено в кластерном режиме, создавая столько воркеров, сколько есть ядер у CPU.

4.1. Балансировка нагрузки с помощью кластерного модуля

Встроенный модуль Node.js позволяет создавать рабочие процессы, обслуживающие ваше приложение. Он основан на реализации child_process и прост в настройке. Вам нужно лишь добавить файл cluster.js и вставить в него следующий код:

Когда вы запустите cluster.js c нодой cluster.js , модуль кластера обнаружит, что он работает как Master-процесс и вызовет функцию masterProcess() . Она подсчитает, сколько процессорных ядер имеет сервер, и вызовет cluster.fork() . Все эти процессы выполняются на одном и том же порту. Подробнее об этом читайте в официальном хелпе.

Слушатель событий cluster.on(‘exit’) перезапустит рабочий процесс, если он завершится неудачей.

Теперь отредактируем поле ExecStart в приложении fooapp.service . Замените:

Перезагрузите Systemd и приложение fooapp.service :

Вы добавили балансировку нагрузки в Express-приложение. Это будет работать только для single-server. Если необходимо несколько серверов – используйте Nginx.

4.2. Добавление обратного прокси с помощью Nginx

Одно из основных правил в работе с приложениями Node.js – не вешайте их на порты 80 и 443. Для перенаправления трафика используйте обратный прокси. Nginx – самый распространённый инструмент для достижения этой цели.

Установка Nginx довольно проста, для Ubuntu это будет выглядеть так:

Если у вас другая ОС, ознакомьтесь с инструкциями по установке Nginx.

Nginx должен стартовать сразу же, но на всякий случай проверьте:

Если не запустился – выполните эту команду:

После запуска Nginx необходимо отредактировать конфиг, чтобы включить обратный прокси. Конфиг Nginx находится в каталоге /etc/nginx/ . Основной конфигурационный файл называется nginx.conf , но есть разные дополнения в каталоге etc/nginx/sites-available/ . Конфигурация сервера по умолчанию находится здесь и называется default.

Чтобы включить обратный прокси-сервер, откройте файл конфигурации по умолчанию и отредактируйте его следующим образом:

Сохранитесь и перезапустите службу Nginx:

Эта настройка будет роутить весь трафик с порта 80 на ваше Express-приложение.

4.3. Кэширование в nginx

Кэширование важно для сокращения времени отклика ресурсов, которые редко изменяются.

Откройте дефолтный конфиг и добавьте эти строки кода:

4.4. Gzip

В серверном блоке конфига добавьте следующие строки:

Если нужно больше информации – читайте официальный хелп.

4.5. Включение кэширования на Redis

Redis – in-memory хранилище, которое часто используется в качестве кэша.

Установка на Ubuntu проста:

Откройте файл /etc/redis/redis.conf и измените одну важную строку:

Перезапустите службу Redis:

Затем установите модуль redis для доступа к Redis из приложения:

Теперь вы можете начать кэшировать запросы. Рассмотрим пример:

Этот код будет кэшировать ответ из БД в виде строки JSON в Redis в течение 3600 секунд. Вы можете изменить это в зависимости от требований.

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