Создание приложений JavaScript с помощью Node.js
Node.js предоставляет большой набор встроенных API, которые помогают создавать различные типы приложений, приложения командной строки, веб-приложения и многое другое. Он также предлагает возможности тестирования и отладки и обширную экосистему пакетов сторонних разработчиков, которые можно легко добавлять в приложение.
Предварительные требования
Базовое понимание JavaScript
PM2: подходим к вопросу процесс-менеджмента с умом
Буквально пару часов назад у меня завязался спор на тему того, что Node.JS слишком медленная для крупных проектов и ей стоит предпочесть Golang, Rust, PHP, etc. Основным аргументом противоположной стороны в этом споре был факт однопоточности JavaScript. Якобы при разработке приложения производительность просто упрётся в эту однопоточность и ничего сделать уже нельзя — только переписать на каком-то другом языке. Однако дела с этим в NodeJS обстоят немного лучше, чем кажется на первый взгляд. Перед тем, как мы углубимся в эту тему хочу заявить, что уважаю право каждого разработчика использовать тот язык программирования, который пришёлся ему по душе и который он считает предпочтительным в той или иной задаче.
Сделав поиск по ключевому слову «PM2» на Хабре я не нашёл ни одной статьи, посвящённой этому process-менеджеру. Лишь одиночные упоминания в статьях других пользователей. Я загорелся (сильно сказано) идеей наверстать упущенное и пролить свет на этот тёмный уголок разработки backend на Node.JS (о котором многие знают, да, я в курсе). Всех заинтересовавшихся прошу под кат.
Пару слов о самом PM2
PM2 — это менеджер процессов с открытым исходным кодом, распространяющийся под лицензией AGPL-3.0. В момент написания статьи имеет ~350k загрузок в неделю, согласно данным NPM. В основном применяется в средах, где необходимо запустить приложение на NodeJS и забыть о нём (с остальными языками тоже можно использовать, но об этом позднее), позволяющий кластеризировать приложение и гибко распределять нагрузку между ядрами процессора. Небольшая вырезка из репозитория PM2 на GitHub:
PM2 is a production process manager for Node.js applications with a built-in load balancer. It allows you to keep applications alive forever, to reload them without downtime and to facilitate common system admin tasks.
Многие новички при разработке сталкиваются с проблемой, когда после «выкатки» приложения на production сервер, не знают как запустить его «навечно». Пишут в SSH-консоли set NODE_ENV=production && node app.js , всё отлично, приложение работает. Закрывают консоль и приложение больше не работает. Вопрос на StackOverflow — How to run node.js application permanently? набрал более 237 тыс. просмотров за всё время.
PM2 решает эту проблему одной командой:
pm2 start app.js
Эта команда «демонизирует» (от англ. «daemonize») процесс NodeJS, следит за потреблением им памяти и считает нагрузку на процессор.
Вернёмся к нашим баранам
С ростом нагрузки на backend возникает необходимость его масштабирования — как вертикального, так и горизонтального — кому что удобнее в сложившихся обстоятельствах. Как мы знаем, один процесс может использовать несколько ядер процессора, но только в том случае, если внутри процесса имеется несколько потоков. В NodeJS приложениях поток — один. PM2 способен выручить в этой ситуации и распределить нагрузку между несколькими ядрами процессора. По-прежнему всего с одной командой:
pm2 start app.js -i max
В данном случае параметр max соответствует количеству ядер процессора. Т.е. для 8-ядерного процессора будет создано 8 отдельных процессов. Можно также вместо max задать значение -1 и тогда количество процессов будет соответствовать количество_ядер минус 1. Вся прелесть заключается в том, что и HTTP(S)/Websocket/TCP/UDP соединения будут равномерно распределены между этими процессами. Ну чем не горизонтальное масштабирование? Почитать подробнее о кластеризации в PM2 можно по ссылке — PM2 Cluster Mode.

Вы можете запустить сколько угодно процессов, но всё же рекомендуется придерживаться рекомендации «один процесс на одно ядро».
Бережное отношение к памяти
При разработке на PHP я однажды столкнулся с проблемой. По неопытности неосознанно заложил в движок системы баг, из-за которого при определённых условиях процессы начинали поедать слишком много оперативной памяти. Вдобавок к этому нагружался процессор, из-за чего виртуальная машина просто зависла и у меня не было к ней доступа совсем.
Как знают PHP-разработчики, в PHP-FPM можно задать тип распределения процессов (если вы вдруг не знали, то в PHP-FPM для каждого нового запроса создаётся новый процесс) — статический, когда задаётся минимальный и максимальный порог, и динамический — выделение сколь угодно большого количества процессов, по необходимости. Что будет в PM2, если запустить 8 процессов и все они начнут потреблять много памяти? И эту проблему PM2 в состоянии решить — лишь одним параметром в командной строке:
# Set memory threshold for app reload pm2 start app.js -i max --max-memory-restart
Каждый раз при достижении лимита по памяти PM2 автоматически перезапустит процесс. Распределять память проще чем процессы, не так ли? 8 процессов * 200 мегабайт = 1,6 гигабайт. Математика уровня второго класса.
Помимо перезапуска процесса можно также настроить и перезапуск через N интервал времени. Я пока не придумал в каких случаях это может пригодиться, но не стесняйтесь указать мне на пару примеров в комментариях 🙂
А если я перезагружу виртуальную машину?
Сюрприз-сюрприз! Эту проблему PM2 тоже решает за вас. Всё ещё не более чем одной единственной командой в консоли:
pm2 startup
PM2 сгенерирует скрипт, который будет поднимать все необходимые процессы при запуске операционной системы. Однако здесь стоит быть бдительным — при обновлении версии Node.JS всё может сломаться. Во избежание этого, после успешного обновления до новой версии Node.JS выполните pm2 unstartup и pm2 startup . Подробнее об этом можно ознакомиться по ссылке — PM2 Startup Script Generator.
А надо ли перезапускать кластеры вручную при внесении изменений?
Конечно же нет! Ну, точнее, вы, конечно, можете перезапускать приложение вручную, но зачем? Автоматизируйте всё что можете и да прибудет с вами сила!
pm2 start env.js --watch --ignore-watch="node_modules"
Вы можете пользоваться этим при слиянии master-ветки в локальном репозитории с master-веткой из удалённого репозитория. В моём сайд-проекте это делается просто — git pull origin master && npm run build . При изменении файлов в папках server/build и client/build процессы будут автоматически перезапущены. Я понимаю, это очень простенькая фича и она не заслуживает даже быть упомянутой в этом тексте. Разбавлю его кое-чем серьёзным и напишу о том, что если вы пользуетесь кластеризацией, то все процессы будут перезагружены поочерёдно. Да так, что как минимум один из них будет всегда доступен. Это же zero-downtime deployment!
А можно и не перезапускать процессы. Для этого есть reload (нечто похожее на nginx reload):
pm2 reload all
Слишком много команд! И вообще я предпочитаю конфиги
Мне уже наскучило придумывать весёлые фразы, поэтому просто и банально: файл экосистемы — есть. Поддерживаются форматы JSON, YAML и JS. Например, когда необходимо следить за файлами в папках server и client:
module.exports = < apps: [< script: "app.js", watch: ["server", "client"], env_production : < "NODE_ENV": "production" >>] >
Подробнее ознакомиться можно по ссылке — PM2 Application Declaration.
И даже мониторинг есть!
И не один. Выбирайте тот который нравится больше. Можно мониторить в консоли командой:
pm2 monit

Или же воспользоваться полноценной веб-версией мониторинга:

Вы мне, конечно же, не поверите, но она устанавливается и запускается одной командой:
pm2 plus
И многое-многое другое.
Заявлена поддержка Heroku и Docker, автоматическое инкрементирование портов с возможностью передачи в process.env (когда нужно каждый процесс запускать на отдельном порту), запуск нескольких инстансов PM2 в пределах одной ОС, наличие программного API и возможность запускать демонизированные Bash и Python скрипты!
Вероятно, я упустил ещё что-то важное или интересное, о чём всегда можно напомнить мне в комментариях. Надеюсь, что вы смогли почерпнуть для себя что-то новое из этой статьи.
Как использовать Docker в приложениях Node.js и React
В этой статье поговорим о том, как разработать и запустить бэкэнд на Node.js-Express и фронтенд на React в контейнере Docker.

Docker дает разработчикам возможность помещать свои приложения в контейнеры. Эти контейнеры могут работать на любом устройстве, на котором установлен Docker, и приложения будут идентичны. С помощью этого сервиса можно клонировать кодовую базу на нескольких системах – везде будет одинаковый результат.
В рабочих процессах CI/CD и средах тестирования DevOps широко используется Docker: по сути, он представляет собой набор программных инструментов, которые можно использовать совместно. Kubernetes — это инструмент, который используется для работы с несколькими контейнерами Docker, но в гораздо большем масштабе.
Разберём, как разработать и запустить бэкэнд на Node.js-Express и фронтенд на React внутри контейнера Docker.
Запуск бэкенда на Node.js-Express в Docker
Когда убедились, что на компьютере установлен и запущен Docker, переходим к следующему шагу.
Используя командную строку, переходим в каталог, например на рабочий стол, а затем выполняем следующие команды.
mkdir my-app-docker cd my-app-docker touch docker-compose.yml mkdir api cd api npm init -y npm i express touch app.js Dockerfile .dockerignore cd ..
Мы настроили серверную часть – api и создали несколько файлов Docker. Теперь откройте проект в редакторе кода и добавьте приведенный ниже код в соответствующие файлы.
Это нужно поместить в файл docker-compose.yml. Обращайте внимание на форматирование YAML, иначе при запуске Docker выдаст ошибку.
version: '3.8' services: api: build: ./api container_name: api_backend ports: - '4000:4000' volumes: - ./api:/app - ./app/node_modules
Следующий код нужно добавить в файл app.js:
const express = require('express'); const app = express(); const port = process.env.PORT || 4000; app.get('/', (req, res) => < res.send('Home Route'); >); app.listen(port, () => console.log(`Server running on port $, http://localhost:$`) );
Эту строку – в файл .dockerignore :
node_modules
Этот код – в файл Dockerfile:
FROM node:16-alpine WORKDIR /app COPY package.json . RUN npm install COPY . . EXPOSE 4000 CMD ["node", "app.js"]
Наконец, в файл package.json:
"scripts": < "start": "node app.js" >,
Использование Nodemon для автоматического перезапуска сервера при внесении изменений.
Если вы хотите, чтобы сервер перезагружался каждый раз, когда вы вносите изменения в файлы в бэкэнде, вы можете настроить его на использование Nodemon. Вам нужно лишь обновить файлы Dockerfile и package.json внутри папки api.
Обновите файл Dockerfile, используя приведенный ниже код. Мы установим Nodemon при запуске и используем dev в качестве команды запуска.
FROM node:16-alpine RUN npm install -g nodemon WORKDIR /app COPY package.json . RUN npm install COPY . . EXPOSE 4000 CMD ["npm", "run", "dev"]
Добавьте в файл package.json скрипт для Nodemon.
"scripts": < "start": "node app.js", "dev": "nodemon -L app.js" >,
Мы только что создали базовое приложение Node.js-Express, которое работает на порте 4000. Этот порт также связан с 4000 в Docker, что позволяет нам запускать его в Docker-контейнере.
Запуск серверов
Чтобы запустить сервер вне контейнера Docker как обычно на Node, просто запустите приведенный ниже код в командной строке. Вы должны быть уверены, что находитесь внутри папки api. Если вы перейдете на http://localhost:4000, вы должны увидеть основной пусть (home route) в окне браузера.
npm run start
Для запуска приложения Node.js-Express внутри Docker требуется другая команда. Во-первых, вам нужно находиться в корневой папке, где находится файл docker-compose.yml. Теперь запустите приведенную ниже команду, и она должна работать внутри контейнера Docker.
Важно отследить, что сервер узла не запущен, потому что на порте 4000 может работать только один сервер.
docker-compose up
По адресу http:// localhost :4000 вы должны видеть основной (домашний) путь.
Вы можете остановить сервер с помощью приведенной ниже команды либо перейти в приложение Docker и остановить запуск контейнера.
docker-compose down
Запуск фронтенда на React в Docker
Теперь давайте создадим фронтенд на React. Используя командную строку, переместитесь в корневую папку для my-app-docker. Запустите приведенные ниже команды, чтобы настроить проект.
npx create-react-app client cd client touch .dockerignore Dockerfile
Теперь добавьте код ниже в соответствующие файлы.
Эту строку – в файл .dockerignore .
node_modules
Этот код – в файл Dockerfile .
FROM node:17-alpine WORKDIR /app COPY package.json . RUN npm install COPY . . EXPOSE 3000 CMD ["npm", "start"]
Наконец, добавьте в файл docker-compose.yml в корневой папке приведенный ниже код. Таким образом мы добавили клиентский раздел внизу с настройками для запуска React внутри контейнера Docker. Обращайте внимание на форматирование YAML, иначе при запуске Docker выдаст ошибку.
version: '3.8' services: api: build: ./api container_name: api_backend ports: - '4000:4000' volumes: - ./api:/app - ./app/node_modules client: build: ./client container_name: client_frontend ports: - '3000:3000' volumes: - ./client:/app - ./app/node_modules stdin_open: true tty: true
Чтобы запустить сервер вне контейнера Docker как обычно на Node, запустите приведенный ниже код в командной строке. Убедитесь, что вы находитесь внутри папки клиента. По адресу http://localhost:3000 вы должны увидеть основной (домашний) путь в окне браузера.
npm run start
Для запуска приложения React внутри Docker требуется другая команда. Во-первых, вам нужно находиться в корневой папке, где расположен файл docker-compose.yml. Теперь запустите приведенную ниже команду, и она должна работать внутри контейнера Docker.
Важно отследить, что сервер приложения React не запущен, потому что на порте 3000 может работать только один сервер.
docker-compose up
По адресу http://localhost:3000 вы должны увидеть основной (домашний) путь в окне браузера.
Вы можете остановить сервер с помощью приведенной ниже команды либо перейти в приложение Docker и остановить запуск контейнера.
docker-compose down
С такими настройками вы можете одновременно запускать бэкенд Node.js и фронтенд React внутри Docker. Если вы столкнулись с какими-либо ошибками, возможно, поможет открыть настольное приложение Docker и удалить все образы, связанные с этим проектом. Затем попробуйте снова запустить команду docker-compose up – на этот раз все должно работать как положено.
Надеемся, этот перевод был для вас полезен!
- Блог компании РСХБ.цифра (Россельхозбанк)
- Веб-разработка
- JavaScript
- Node.JS
- ReactJS
Десктопные приложения на JavaScript. Часть 1
Ни для кого не секрет, что в наше время JavaScript стал одним из самых популярных языков программирования. В далекие 90е годы, в момент зарождения языка, когда он был создан с единственной целью добавить интерактивность веб страницам и улучшить процесс взаимодействия с пользователем, кто бы мог подумать, что он достигнет столь небывалых высот. Ведь сейчас на нем можно делать практически все что угодно. Хотите написать сайт: и бэкэнд и фронтэнд на JavaScript? пожалуйста! Хотите написать мобильное приложение на JavaScript? нет проблем. Программируете микроконтроллер – и тут вам на помощь придет JavaScript.
Есть конечно небольшие минусы в подходе использования JavaScript везде, но если поразмыслить, то сколько времени и сил можно сэкономить, изучив всего лишь одни язык, особенно, если то же самое приложение должно работать на разных платформах. Разных платформах говорите? Хм… Точно – разных платформах – теперь JS может позволить себе десктопные приложения для Windows, Linux, Mac, как спросите вы? Ответ прост: встречайте – NW.js.
По первым буквам можно прочитать – Node.js + Webkit, если данные понятия вам пока не знакомы, то скоро вы поймете о чем идет речь.
Node.js – программная платформа, основанная на движке V8, который транслирует наш скрипт в машинный код. Данная платформа была создана в 2009 году преимущественно для работы с бэкэндом сайтов.
WebKit — свободный движок, разработанный компанией Apple. Впервые был анонсирован в составе Safari в 2003 году
Итак, коду, написанному на JS для данной технологии, будут доступны как Node.js модули, так и стандартный браузерный API (соответственно WebKit)
Быстрый старт
Все это конечно хорошо, но с чего же начать? На github можно найти и скачать репозиторий с исходным кодом. Так же здесь можно найти прямые ссылки для скачивания под ту платформу, на которой будет вестись разработка. Помимо прочего нам понадобится установленная node.js.
После того, как необходимое ПО скачано и установлено, вы написали свое приложение на любимом JS (как это сделать читайте далее) и локализовали все в одну папку. Полдела сделано, теперь остается самое сложное и долгое – упаковать все в один файл и подготовить для распространения. Для упрощения вы можете воспользоваться готовыми библиотеками, например nw-builder. Установка библиотеки не составит труда, если вы уже работали с node.js. Как известно, в состав node.js входит менеджер пакетов npm, с которым нужно работать из командной строки. Для того, чтобы поставить какую-либо библиотеку, необходимо выполнить команду:
> npm install [имя_библиотеки] [опции]
Обратите внимание, что библиотеку можно ставить, как локально, так и глобально, для локальной установки используйте опцию —save-dev, для глобальной -g. Таким образом поставим наш сборщик для NW.js глобально, выполнив команду:
> npm install nw-builder -g
Для того, чтобы собрать наше приложение, необходимо выполнить команду (с большим количеством опций можно ознакомиться в документации):
> nwbuild -p [имя_платформы] -o [путь_к_папке_для_собранной_версии] [путь_до_приложения]
В качестве имени платформы могут быть следующие значения: win32, win64, osx32, osx64, linux32, linux64.
Во время разработки нет нужды каждый раз собирать приложение, можно просто запустить его как есть и оно откроется в отдельном окне. Для этого нужно запустить приложение nw.exe из командной строки и передать в качестве параметров путь к папке с вашим приложением. Кроме того, если вы работаете под Windows, можно просто методом drag-n-drop перетащить папку с исходным кодом приложения на JS (обратите внимание, что именно папку целиком) в nw.exe.
Hello, world!
Теперь, когда вы знаете, как запустить приложение, как собрать его в один файл, давайте напишем что-нибудь. По традиции знакомство с новой платформой начинается с написания приложения Hello, world.
Для данного приложения, нам даже не понадобится JavaScript, только HTML. Создадим папку с названием HelloWorld. Поместим внутрь файл index.html со следующей разметкой:
Hello, world Hello, world, from NW.js
Кроме того для каждого приложения под NW.js необходим файл, который обязательно должен называться package.json. Из него будет браться информация для построения приложения. Создадим простейший вариант файла и поместим в папку HelloWorld. Итак:
Содержимое файла понятно без пояснений (обратите внимание, что обязательные поля только main и name). В main необходимо записать файл с разметкой, который будет являться точкой входа в приложение. Секция window настраивает параметры окна (в данном случае мы отключаем панель инструментов и задаем размеры окна 500×200).
Кроме того, можно настроить такие поля как (за полным списком опций обращайтесь в документацию):
- icon – указываем путь до иконки (переопределить стандартную)
- position – можно указать позицию окна при загрузке (null, center или mouse)
- min_width, min_height, max_width, max_height – ограничение размеров окна
- resizable – логическое значение, которое показывает можно ли пользователю изменять размеры окна
- fullscreen – включить полноэкранный режим
- kiosk – включить режим киоска
- transparent – сделать окно прозрачным

Приложение написано, но в нем всего один div элемент и совсем нет логики, а что делать, если у нас богатая на элементы разметка и сложная логика? На помощь к нам приходит элемент конфигурационного файла toolbar, который мы установили в false. Для того, чтобы сделать доступными средства отладки, необходимо установить toolbar в true. Проделав это при запуске приложения мы получим следующее окно:

После нажатия на кнопку в верхнем правом углу откроется еще одно окно, в котором будут отображены знакомые инструменты разработчика:

Работа с нативными контролами
NW.js позволяет работать с нативными контролами. Рассмотрим работу на примере меню. Для работы с нативным UI контролами в nw.js необходимо использовать модуль nw.gui, который можно подключить следующим образом:
var gui = require('nw.gui');
Общий шаблон для использования контролов:
var element = new gui.ElementName(option);
Таким образом для создания элементов меню можно воспользоваться следующей конструкцией:
var menu = new gui.Menu();
Кроме того любые свойства созданного нами объекта можно легко изменить стандартными конструкциями JS, например так:
menu.title = 'New Title';
Меню создано, теперь нужно его заполнить, для манипуляции дочерними элементами существуют методы:
menu.append(new gui.MenuItem()); menu.removeAt(0);
Кроме того для более гибкого добавления элементов в menu можно воспользоваться методом insert, в параметрах которого необходимо передать MenuItem и номер позиции, куда его вставить (позиция перед первым элементом соответствует 0).
Для доступа к созданным элементам можно использовать свойство items:
menu.items[0].title = "New title"
Обратите внимание, что нельзя напрямую создавать элементы:
menu.items[2] = new gui.MenuItem(); // НЕПРАВИЛЬНО
Самое главное при работе с нативными контролами, это помнить, что любая ошибка при работе с ними может привести к краху всего приложения, поэтому необходимо быть крайне внимательными и по возможности при удалении элементов, также присваивать переменной значение null. Таким образом для удаления контрола, можно выполнить следующее:
control.remove(); control = null;
Для более удобной работы с контролами, они унаследованы от EventEmitter, поэтому хорошая новость в том, что мы можем легко работать с событиями, например так:
menuitem.on('click', function() < // сделать что-нибудь полезное >);
Меню было создано, но если запустить приложение, то никакого меню вы не увидите. Для отображения меню существует метод popup, в параметрах которого необходимо передать координаты для отображения меню.
Для демонстрации основных возможностей меню добавьте следующий скрипт к созданному ранее проекту Hello, world:
var gui = require('nw.gui'); var menu1 = new gui.Menu(); menu1.append(new gui.MenuItem()); var subMenu1 = new gui.Menu(); subMenu1.append(new gui.MenuItem()); menu1.append(new gui.MenuItem(< label: "Submenu", submenu: subMenu1 >)); document.body.addEventListener('contextmenu', function(ev) < ev.preventDefault(); menu1.popup(ev.x, ev.y); return false; >);
После запуска приложения, мы можем увидеть созданное контекстное меню для body. Таким образом, мы можем определить контекстное меню для любого элемента.

Итак, теперь кроссплатформенные приложения может создавать каждый, но за все нужно платить. В данном случае мы жертвуем как скоростью, так и занимаемым объемом памяти (собранное приложение получается достаточно большим, более 50 Мб). Список приложений, созданных, используя данную технологию можно найти на github.
Во второй части статьи мы рассмотрим технологию более подробно.