Вчера друг спросил, как развернуть экспресс-проект. Поэтому я составил эту статью, в которой в основном рассказывается о том, как развернуть серверную программу, разработанную на основе nodejs, для помощи нуждающимся друзьям.
Статья состоит из нескольких частей:
Процесс (процесс) — это компьютер. Распределение операционной системы и Основная единица планирования задач . Откройте диспетчер задач, и вы увидите, что на самом деле в фоновом режиме компьютера работает множество программ, и каждая программа представляет собой процесс.
Современные браузеры в основном имеют многопроцессную архитектуру. На примере браузера Chrome откройте «Дополнительные инструменты» — «Диспетчер задач», и вы увидите информацию о процессе текущего браузера. Одна из страниц — это процесс, за исключением. Кроме того, существуют сетевые процессы, процессы графического процессора и т. д.
Многопроцессная архитектура обеспечивает более стабильную работу приложения. Если взять в качестве примера браузер, то если все программы запускаются в одном процессе, то в случае сбоя сети или ошибки рендеринга страницы это приведет к сбою всего браузера. Благодаря многопроцессной архитектуре, даже если сетевой процесс выйдет из строя, это не повлияет на отображение существующих страниц, а в худшем случае — временно не сможет получить доступ к сети.
Поток — это наименьшая единица, с помощью которой операционная система может выполнять планирование вычислений . Он включен в процесс и является фактической операционной единицей процесса. Например, программа похожа на компанию с несколькими отделами, которые представляют собой процессы; сотрудничество каждого отдела позволяет компании нормально работать, а потоки — это сотрудники, люди, выполняющие конкретную работу.
Мы все знаем, что JavaScript — однопоточный язык. Такой дизайн обусловлен тем, что на заре JS в основном использовался для написания скриптов и отвечал за реализацию интерактивных эффектов страницы. Если он спроектирован как многопоточный язык, во-первых, в этом нет необходимости, а во-вторых, несколько потоков совместно управляют DOM-узлом, то чьим советам должен прислушиваться браузер? Конечно, с развитием технологий JS теперь тоже поддерживает многопоточность, но она используется только для обработки некоторой логики, не связанной с операциями DOM.
Одиночные потоки и отдельные процессы создают серьезную проблему. Как только основной поток работающей программы node.js зависает, процесс также зависает, и все приложение также зависает. Кроме того, большинство современных компьютеров имеют многоядерные процессоры с четырьмя ядрами и восемью потоками, а также восемь ядер и шестнадцать потоков, которые являются очень распространенными устройствами. Будучи однопроцессной программой, node.js тратит впустую производительность многоядерных процессоров.
В ответ на эту ситуацию нам нужна подходящая многопроцессная модель для преобразования однопроцессной программы node.js в многопроцессную архитектуру.
Существует два распространенных решения для реализации многопроцессной архитектуры в Node.js, оба из которых используют собственные модули, а именно модуль child_process
и модуль cluster
.
child_process
— это встроенный модуль node.js. По названию можно догадаться, что он отвечает за вещи, связанные с дочерними процессами.
Мы не будем подробно останавливаться на конкретном использовании этого модуля. На самом деле в нем всего около шести или семи методов, которые очень легко понять. Мы используем один из методов fork
, чтобы продемонстрировать, как реализовать несколько процессов и связь между несколькими процессами.
Давайте сначала посмотрим на структуру каталогов подготовленного демонстрационного примера:
Мы используем модуль http
для создания http-сервера. При поступлении запроса /sum
дочерний процесс будет создан с помощью модуля child_process
, и дочерний процесс будет уведомлен о необходимости выполнения логики вычислений. также должен прослушивать сообщения, отправленные дочерним процессом:
//child_process.js const http = require('http') const { fork } = require('child_process') const server = http.createServer((req, res) => { if (req.url == '/sum') { // Метод fork получает путь к модулю, затем запускает дочерний процесс и запускает модуль в дочернем процессе // childProcess представляет созданный дочерний процесс let childProcess = fork('./sum.js') //Отправляем сообщение дочернему процессу childProcess.send('Дочерний процесс начинает расчет') // Мониторинг сообщений дочернего процесса в родительском процессе childProcess.on('message', (data) => { res.end(данные + '') }) //Прослушиваем событие закрытия дочернего процесса childProcess.on('close', () => { // Если дочерний процесс завершается нормально или сообщает об ошибке и зависает, он будет помещен сюда console.log('дочерний процесс закрывается') дочернийПроцесс.kill() }) //Прослушиваем событие ошибки дочернего процесса childProcess.on('error', () => { console.log('ошибка дочернего процесса') дочернийПроцесс.kill() }) } if (req.url == '/привет') { res.end('привет') } // Имитируем родительский процесс, чтобы сообщить об ошибке if (req.url == '/error') { выдать новую ошибку («Ошибка родительского процесса») res.end('привет') } }) server.listen(3000, () => { console.log('Сервер работает на 3000') })
sum.js
используется для моделирования задач, которые должен выполнять дочерний процесс. Дочерний процесс слушает сообщения, отправленные родительским процессом, обрабатывает задачи расчета, а затем отправляет результаты родительскому процессу:
// sum.js функция getSum() { пусть сумма = 0 for (пусть я = 0; я < 10000 * 1000 * 100; я++) { сумма += 1 } возвратная сумма } // процесс — это глобальный объект в node.js, представляющий текущий процесс. Вот это дочерний процесс. // Прослушиваем сообщения, отправленные основным процессомprocess.on('message', (data) => { console.log('Сообщение от основного процесса:', данные) константный результат = getSum() //Отправляем результаты расчета родительскому процессуprocess.send(result) })
Откройте терминал и запустите команду node 1.child_process
:
Зайдите в браузер:
Далее смоделируйте ситуацию, когда дочерний процесс сообщает об ошибке:
// sum.js функция getSum() { // .... } // После того, как дочерний процесс проработает 5 секунд, процесс моделирования зависает setTimeout(() => { выдать новую ошибку («отчет об ошибке») }, 1000*5) process.on('сообщение', (данные) => { // ... })
Снова зайдите в браузер и наблюдайте за консолью через 5 секунд:
Дочерний процесс умер, а затем обращается к другому URL-адресу: /hello
,
Видно, что родительский процесс все еще может правильно обработать запрос, что указывает на то, что ошибка, о которой сообщает дочерний процесс, не повлияет на работу родительского процесса .
Далее мы смоделируем сценарий, в котором родительский процесс сообщает об ошибке, закомментируем смоделированный отчет об ошибке модуля sum.js
, затем перезапустим службу и получим доступ к /error
в браузере:
Обнаружив, что родительский процесс завис, вся программа node.js автоматически завершила работу, а сервис полностью рухнул, не оставив места для восстановления.
Видно, что реализовать многопроцессную архитектуру node.js с помощью метода fork
child_process
несложно. Межпроцессное взаимодействие осуществляется в основном через методы send
и on
. Из этого именования мы также можем понять, что нижний уровень должен представлять собой модель публикации-подписки.
Но у него есть серьезная проблема. Хотя дочерний процесс не влияет на родительский процесс, как только родительский процесс допустит ошибку и зависнет, все дочерние процессы будут «убиты одновременно». Поэтому данное решение подходит для вынесения некоторых сложных и трудоемких операций в отдельный подпроцесс . Точнее, это использование используется для замены реализации многопоточности, а не многопроцессорности.
использует модуль child_process
для реализации нескольких процессов, что кажется бесполезным. Поэтому обычно рекомендуется использовать модуль cluster
для реализации многопроцессной модели node.js.
cluster
означает «кластер», я думаю, этот термин знаком всем. Например, раньше у компании была только одна стойка регистрации, и иногда она была слишком загружена, чтобы вовремя принять посетителей. Теперь компания выделила четыре стойки регистрации. Даже если три из них заняты, все равно есть одна, которая может принять новых посетителей. Кластеризация примерно означает это. Для одной и той же задачи разумно поручать ее выполнение разным людям, чтобы гарантировать, что эта задача может быть выполнена наилучшим образом.
Использование модуля cluster
также относительно просто. Если текущий процесс является основным, создайте соответствующее количество подпроцессов в зависимости от количества ядер ЦП и прослушайте событие exit
подпроцесса. Если подпроцесс завершается, повторно создайте новый подпроцесс. -процесс. Если это не дочерний процесс, обрабатывается реальный бизнес.
const http = require('http') const кластер = require('кластер') const cpus = require('os').cpus() если (cluster.isMaster) { // Когда программа запускается, она сначала идет сюда и создает несколько подпроцессов в соответствии с количеством ядер ЦП for (let i = 0; i < cpus.length; i++) { //Создаем дочерний процессuster.fork() } // Когда какой-либо дочерний процесс зависает, модуль кластера выдает событие выхода. На этом этапе процесс перезапускается повторным вызовом fork. Cluster.on('exit', () => { кластер.форк() }) } еще { // Метод fork выполняется для создания дочернего процесса, и модуль будет выполнен снова. В это время сюда придет логика const server = http.createServer((req, res) => {. console.log(process.pid) res.end('ок') }) server.listen(3000, () => { console.log('Сервер работает на 3000', 'pid:' +process.pid) }) }
Запустите службу:
Как видите, модуль cluster
создал множество дочерних процессов, и кажется, что каждый дочерний процесс запускает один и тот же веб-сервис.
Следует отметить, что эти дочерние процессы в настоящее время не прослушивают один и тот же порт . Сервер, созданный методом createServer, по-прежнему отвечает за мониторинг портов и перенаправляет запросы каждому дочернему процессу.
Давайте напишем сценарий запроса для запроса вышеуказанной услуги и посмотрим на результат.
// запрос.js const http = require('http') for (пусть я = 0; я < 1000; я++) { http.get('http://localhost:3000') }
Модуль http может не только создавать http-сервер, но также использоваться для отправки http-запросов. Axios поддерживает браузерную и серверную среду. На стороне сервера модуль http используется для отправки http-запросов.
Используйте команду node
для выполнения файла и посмотрите на исходную консоль:
Выводятся идентификаторы процессов различных подпроцессов, которые конкретно обрабатывают запрос.
Это многопроцессная архитектура nodd.js, реализованная через модуль cluster
.
Конечно, когда мы разворачиваем проекты node.js, мы не будем так сухо писать и использовать модуль cluster
. Существует очень полезный инструмент под названием PM2 , который представляет собой инструмент управления процессами на основе модуля кластера. Его основное использование будет представлено в последующих главах.
До сих пор мы посвятили часть статьи знакомству с многопроцессорностью в node.js. На самом деле мы просто хотим объяснить , почему нам нужно использовать pm2 для управления приложениями node.js. Из-за ограниченного объема статьи и отсутствия точного/подробного описания в ней дается лишь краткое введение. Если вы впервые сталкиваетесь с этим контентом, возможно, вы его не очень хорошо понимаете, поэтому не волнуйтесь, позже будет более подробная статья.
В этой статье подготовлен пример программы, разработанной с использованием экспресс-версии. Нажмите здесь, чтобы получить доступ.
В основном он реализует интерфейсную службу. При доступе к /api/users
mockjs
используется для имитации 10 фрагментов пользовательских данных и возврата списка пользователей. В то же время будет запущен таймер, моделирующий ситуацию ошибки:
const express = require('express') const Mock = require('mockjs') константное приложение = экспресс() app.get("/api/users", (req, res) => { const userList = Mock.mock({ 'userList|10': [{ 'id|+1': 1, 'имя': '@cname', 'электронная почта': '@email' }] }) setTimeout(()=> { выдать новую ошибку («Сбой сервера») }, 5000) рез.статус(200) res.json(список пользователей) }) app.listen(3000, () => { console.log("Служба запущена: 3000") })
Проверьте его локально и выполните команду в терминале:
node server.js
Откройте браузер и получите доступ к интерфейсу списка пользователей:
Через пять секунд сервер зависнет:
Эту проблему мы сможем решить позже, когда будем использовать pm2 для управления приложениями.
Обычно после завершения проекта vue/react мы сначала его упаковываем, а затем публикуем. Фактически, интерфейсные проекты необходимо упаковывать главным образом потому, что конечной средой выполнения программы является браузер, и у браузера есть различные проблемы совместимости и проблемы с производительностью, такие как:
.vue
, .jsx
, .ts
необходимо скомпилироватьПроекты, разработанные с использованием express.js или koa.js, не имеют эти проблемы. Более того, Node.js принимает модульную спецификацию CommonJS и имеет механизм кэширования, при этом модуль будет импортироваться только при его использовании ; Если вы упаковаете его в файл, это преимущество фактически будет потрачено впустую. Таким образом, для проектов node.js нет необходимости упаковывать.
В этой статье в качестве примера используется система CentOS, чтобы продемонстрировать,
Чтобы облегчить переключение версий узла, мы используем nvm для управления узлами.
Nvm (Node Version Manager) — это инструмент управления версиями Node.js. С его помощью узел можно произвольно переключать между несколькими версиями, избегая повторных операций загрузки и установки, когда требуется переключение версий.
Официальный репозиторий Nvm — github.com/nvm-sh/nvm. Поскольку сценарий установки хранится на сайте githubusercontent
, он часто недоступен. Поэтому я создал для него новый зеркальный репозиторий на gitee, чтобы иметь доступ к сценарию его установки из gitee.
Загрузите сценарий установки с помощью команды curl
и используйте bash
для его выполнения, который автоматически завершит установку nvm:
# curl -o- https://gitee.com/hsyq/nvm/raw/master/install.sh | bash
Когда установка завершится После этого открываем новое окно для использования nvm:
[root@ecs-221238 ~]# nvm -v0.39.1
может нормально печатать номер версии, указывая на то, что nvm успешно установлен.
Теперь вы можете использовать nvm для установки узла и управления им.
Просмотр доступных версий узла:
# nvm ls-remote
Установочный узел:
# nvm install 18.0.0
Просмотр установленных версий узла:
[root@ecs-221238 ~]# nvm list -> v18.0.0 по умолчанию -> 18.0.0 (-> v18.0.0) iojs -> Н/Д (по умолчанию) нестабильный -> Н/Д (по умолчанию) узел -> стабильный (-> v18.0.0) (по умолчанию) стабильная -> 18.0 (-> v18.0.0) (по умолчанию)
Выберите версию для использования:
# nvm use 18.0.0
Следует отметить, что при использовании nvm в Windows вам необходимо использовать права администратора для выполнения команды nvm. В CentOS я по умолчанию вхожу в систему как пользователь root, поэтому проблем нет. Если при его использовании вы столкнулись с неизвестными ошибками, вы можете найти решения или попытаться выяснить, вызвана ли проблема разрешениями.
При установке узла npm будет установлен автоматически. Проверьте номера версий node и npm:
[root@ecs-221238 ~]# node -v v18.0.0 [root@ecs-221238 ~]# npm -v
Источником образа npm по умолчанию
в версии 8.6.0является официальный адрес:
[root@ecs-221238 ~]# npm config get Registry. https://registry.npmjs.org/
Переключитесь на внутренний источник зеркала Taobao:
[root@ecs-221238 ~]# реестр набора настроек npm https://registry.npmmirror.com
На этом этапе на сервере установлен узел среда и npm настроены.
Существует множество способов: либо загрузка на сервер из репозитория Github/GitLab/Gitee, либо загрузка локально через ftp-инструмент. Шаги очень просты и больше не будут демонстрироваться.
Демо-проект размещается в каталоге /www
:
Как правило, облачные серверы открывают только порт 22 для удаленного входа в систему. Часто используемые порты, такие как 80 и 443, закрыты. Кроме того, подготовленный нами экспресс-проект работает на порту 3000. Итак, вам нужно сначала зайти в консоль облачного сервера, найти группу безопасности, добавить несколько правил и открыть порты 80 и 3000.
мы можем использовать nodemon
для мониторинга в реальном времени и автоматического перезапуска для повышения эффективности разработки. В производственной среде вам нужно использовать убийцу — PM2.
сначала установите pm2 глобально:
# npm i -g pm2
Выполните команду pm2 -v
, чтобы проверить успешность установки:
[root@ecs-221238 ~]# pm2 -v5.2.0
Перейдите в каталог проекта и сначала установите зависимости:
cd /www/express-demo npm install
, а затем используйте команду pm2
для запуска приложения.
pm2 запустить app.js -i max // Или pm2 start server.js -i 2
Приложение управления PM2 имеет два режима: разветвление и кластер. При запуске приложения, используя параметр -i для указания количества экземпляров, автоматически включается режим кластера. На этом этапе доступны возможности балансировки нагрузки.
-i: экземпляр, количество экземпляров. Вы можете написать конкретное число или настроить его на максимум.
PM2
автоматически проверит количество доступных процессоров, а затем запустит как можно больше процессов.
Приложение запущено. PM2 будет управлять приложением в виде процесса-демона. В этой таблице показана некоторая информация о запущенном приложении, например, состояние работы, загрузка ЦП, использование памяти и т. д.
Доступ к интерфейсу в локальном браузере:
Режим кластера — это многопроцессная и многоэкземплярная модель. Когда поступает запрос, он назначается одному из процессов для обработки. Как и в случае с модулем cluster
который мы видели ранее, благодаря защите pm2, даже если процесс завершается, он будет немедленно перезапущен.
Вернитесь к терминалу сервера и выполните команду pm2 logs
для просмотра журналов pm2:
Видно, что экземпляр приложения с идентификатором 1 зависает, и pm2 немедленно перезапустит экземпляр. Обратите внимание, что идентификатор здесь — это идентификатор экземпляра приложения, а не идентификатор процесса.
На этом простое развертывание экспресс-проекта завершено. Используя инструмент pm2, мы можем гарантировать стабильную и надежную работу нашего проекта.
Для справки здесь приведено краткое описание некоторых часто используемых команд инструмента pm2.
# Режим разветвления pm2 start app.js --name app # Установить имя приложения app #Режим кластера# Использовать балансировку нагрузки для запуска 4 процессов pm2 start app.js -i 4 # Запустит 4 процесса с использованием балансировки нагрузки, в зависимости от доступного процессора pm2 запустить app.js -i 0 # Эквивалент эффекта приведенной выше команды pm2 start app.js -i max # Расширьте приложение тремя дополнительными процессами. Приложение масштабирования pm2 +3. # Расширить или сжать приложение до 2 процессов pm2, масштабировать приложение 2 # Просмотр статуса приложения # Отображение статуса всех процессов списка pm2 # Распечатываем список всех процессов в необработанном формате JSON pm2 jlist # Используйте улучшенный JSON для печати списка всех процессов pm2 Prettylist # Отобразить всю информацию о конкретном процессе pm2 описать 0 # Используйте панель мониторинга для мониторинга всех процессов pm2 monit #Управление журналами# Отображение всех журналов приложений. Журналы pm2 в реальном времени. # Отображать журналы приложений приложения в режиме реального времени. Приложение pm2 logs # Используйте формат json для отображения журналов в реальном времени, не выводите старые журналы, выводите только вновь созданные журналы pm2 logs --json #Управление приложениями# Остановить все процессы pm2 остановить все # Перезапускаем все процессы pm2 перезапускаем все # Остановить процесс с указанным идентификатором pm2 stop 0 # Перезапускаем процесс с указанным идентификатором pm2 restart 0 # Удалить процесс pm2 с идентификатором 0 delete 0 # Удалить все процессы pm2 delete all
Вы можете попробовать каждую команду самостоятельно, чтобы увидеть эффект.
Вот специальная демонстрация команды monit
, которая может запускать панель в терминале для отображения рабочего статуса приложения в реальном времени. Все приложения, управляемые pm2, можно переключать с помощью стрелок вверх и вниз:
PM2 имеет очень мощные функции, гораздо больше, чем приведенные выше команды. При реальном развертывании проекта вам также может потребоваться настроить файлы журналов, режим наблюдения, переменные среды и т. д. Было бы очень утомительно каждый раз вводить команды вручную, поэтому pm2 предоставляет файлы конфигурации для управления и развертывания приложений.
Вы можете создать файл конфигурации с помощью следующей команды:
[root@ecs-221238 express-demo]# pm2 init simple Сгенерированный файл /www/express-demo/ecosystem.config.js
создаст файл ecosystem.config.js
:
модуль.экспорт = { приложения : [{ имя: «приложение1», сценарий: "./app.js" }] }
Вы также можете создать файл конфигурации самостоятельно, например app.config.js
:
const path = require('path') модуль.экспорт = { // Один файл конфигурации может управлять несколькими приложениями node.js одновременно // приложения — это массив, каждый элемент — это конфигурация приложения apps: [{ //Имя приложения: "экспресс-демо", // Сценарий файла записи приложения: "./server.js", // Существует два режима запуска приложения: кластер и форк. По умолчанию используется форк. exec_mode: 'кластер', // Количество экземпляров приложения для создания экземпляров: 'max', // Включаем мониторинг и автоматически перезапускаем приложение при изменении файла watch: true, //Игнорировать изменения в некоторых файлах каталога. // Поскольку каталог журналов находится в пути проекта, его следует игнорировать, иначе приложение будет генерировать журналы при запуске. PM2 перезапустится, когда будет отслеживать изменения. Если он перезапустится и создаст журналы, он войдет в бесконечность. цикл ignore_watch: [ "узел_модули", "бревна" ], // Путь хранения журнала ошибок err_file: path.resolve(__dirname, 'logs/error.log'), //Печать пути к хранилищу журналов out_file: path.resolve(__dirname, 'logs/out.log'), //Устанавливаем формат даты перед каждым журналом в файле журнала log_date_format: "ГГГГ-ММ-ДД ЧЧ:мм:сс", }] }
Разрешите pm2 использовать файлы конфигурации для управления приложениями узла:
pm2 start app.config.js
Теперь приложения, управляемые pm2, будут помещать журналы в каталог проекта (по умолчанию — в каталог установки pm2) и смогут отслеживать изменения файлов. , автоматически перезапустите службу.