One place for hosting & domains

      запуск

      Запуск дочерних процессов в Node.js


      Автор выбрал COVID-19 Relief Fund для получения пожертвования в рамках программы Write for DOnations.

      Введение

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

      Node.js включает модуль child_process, который имеет функции для создания новых процессов. Помимо работы с длительными задачами, этот модуль может также взаимодействовать с ОС и запускать команды оболочки. Системные администраторы могут использовать Node.js для запуска команд оболочки для структурирования и поддержания их операций в качестве модуля Node.js вместо скриптов оболочки.

      В этом обучающем руководстве вы создадите дочерние процессы, выполняя серию образцов приложений Node.js. Вы создадите процессы с помощью модуля child_process путем получения результатов дочернего процесса через буфер или строку с функцией exec(), а затем из потока данных с функцией spawn(). Вы закончите, используя fork() для создания дочернего процесса другой программы Node.js, с которой вы можете коммуницировать по мере ее выполнения. Для иллюстрации этих концепций, вы напишете программу, чтобы перечислить содержание каталога, программу для поиска файлов и веб-сервер с несколькими конечными точками.

      Предварительные требования

      Шаг 1 — Создание дочернего процесса с помощью exec()

      Разработчики обычно создают дочерние процессы для выполнения команд в операционной системе, когда необходимо проводить манипуляции с выводом их программ Node.js с помощью оболочки, например при использовании передачи или перенаправления оболочки. Функция exec() в Node.js создает новый процесс оболочки и выполняет команду в этой оболочке. Вывод команды хранится в буфере в памяти, который вы можете принимать с помощью функции обратного вызова, передаваемой в exec().

      Давайте начнем создание первых дочерних процессов в Node.js. Для начала нам нужно настроить среду кодирования для хранения скриптов, которые будут создаваться в данном обучающем руководстве. Создайте в терминале папку с именем child-processes:

      Войдите в эту папку в терминале с помощью команды cd:

      Создайте новый файл с именем listFiles.js и откройте файл в текстовом редакторе. В этом обучающем руководстве мы будем использовать nano, текстовый редактор терминала:

      Мы будем писать модуль Node.js, который использует функцию exec() для запуска команды ls. Команда ls перечисляет файлы и папки в каталоге. Эта программа принимает вывод из команды ls и отображает его пользователю.

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

      ~/child-processes/listFiles.js

      const { exec } = require('child_process');
      
      exec('ls -lh', (error, stdout, stderr) => {
        if (error) {
          console.error(`error: ${error.message}`);
          return;
        }
      
        if (stderr) {
          console.error(`stderr: ${stderr}`);
          return;
        }
      
        console.log(`stdout:n${stdout}`);
      });
      

      Сначала мы импортируем функцию exec() из модуля child_process с помощью деструктурирования JavaScript. После импорта мы используем функцию exec(). Первым аргументом является команда, которую мы хотим запустить. В этом случае это ls -lh, которая перечисляет все файлы и папки в текущем каталоге в длинном формате с общим размером файла в удобочитаемых единицах в верхней части вывода.

      Второй аргумент — это функция обратного вызова с тремя параметрами: error, stdout и stderr. Если команда не запустилась, error выявит причину сбоя. Это может произойти, если оболочка не может найти команду, которую вы пытаетесь выполнить. Если команда выполняется успешно, все данные, которые она записывает в стандартный поток вывода, записываются в stdout, а все данные, которые она записывает в стандартный поток ошибок, записываются в stderr.

      Примечание. Важно запомнить разницу между error и stderr. Если сама команда не запустилась, error запишет ошибку. Если команда выполняется, но возвращает вывод в поток ошибок, stderr запишет ее. Самые устойчивые программы Node.js будут обрабатывать все возможные выводы для дочернего процесса.

      В функции обратного вызова мы сначала проверим, получена ли ошибка. Если получена, мы отобразим message ошибки (свойство объекта Error) с console.error() и завершим функцию с помощью return. Затем мы проверим, напечатала ли команда сообщение об ошибке и return, если это так. Если команда успешно выполняется, мы запишем ее вывод в консоль с помощью console.log().

      Давайте запустим этот файл, чтобы увидеть его в действии. Сначала сохраните и закройте nano, нажав CTRL+X.

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

      Ваш терминал отобразит следующий вывод:

      Output

      stdout: total 4.0K -rw-rw-r-- 1 sammy sammy 280 Jul 27 16:35 listFiles.js

      В нем перечисляется содержимое каталога child-processes в длинном формате вместе с размером содержимого в верхней части. Результаты будут содержать собственного пользователя и группу вместо sammy. Это показывает, что программа listFiles.js успешно запустила команду оболочки ls -lh.

      Теперь давайте рассмотрим другой способ выполнения параллельных процессов. Модуль child_process в Node.js также может запускать исполняемые файлы с функцией execFile(). Ключевая разница между функциями execFile() и exec() заключается в том, что первый аргумент execFile() теперь является путем к исполняемому файлу вместо команды. Вывод исполняемого файла хранится в буфере, например exec(), доступ к которому мы получаем с помощью функции обратного вызова с параметрами error, stdout и stderr.

      Примечание. Скрипты в Windows, например файлы .bat и .cmd, нельзя запустить с помощью execFile(), поскольку функция не создает оболочку при запуске файла. В Unix, Linux и macOS исполняемым скриптам не всегда требуется оболочка для запуска. Однако на компьютерах с Windows требуется оболочка для выполнения скриптов. Для исполнения файлов скрипта в Windows используйте функцию exec(), поскольку она создает новую оболочку. Также вы можете использовать команду spawn(), которую вы будете использовать далее в этом шаге.

      Тем не менее обратите внимание, что вы можете успешно выполнять файлы .exe в Windows с помощью execFile(). Это ограничение распространяется только на файлы скрипта, для выполнения которых требуется оболочка.

      Начнем с добавления исполняемого скрипта для запуска execFile(). Мы напишем скрипт bash, который загрузит логотип Node.js с сайта Node.js, а Base64 зашифрует его для преобразования данных в строку символов ASCII.

      Создайте новый файл скрипта с оболочкой с именем processNodejsImage.sh:

      • nano processNodejsImage.sh

      Теперь напишите скрипт для загрузки образа и его конвертации base64:

      ~/child-processes/processNodejsImage.sh

      #!/bin/bash
      curl -s https://nodejs.org/static/images/logos/nodejs-new-pantone-black.svg > nodejs-logo.svg
      base64 nodejs-logo.svg
      

      Первым выражением является выражение shebang. Оно используется в Unix, Linux и macOS, когда необходимо указать оболочку для выполнения нашего скрипта. Второе выражение — это команда curl. Утилита cURL, чьей командой является curl, — это инструмент командной строки, который может передавать данные на сервер и с него. Мы используем cURL для загрузки логотипа Node.js с сайта, а затем используем перенаправление для сохранения загруженных данных в новый файл nodejs-logo.svg. В последнем выражении используется утилита base64 для кодирования файла nodejs-logo.svg, который мы загрузили с помощью cURL. Затем скрипт выводит закодированную строку в консоль.

      Сохраните и закройте перед продолжением.

      Чтобы наша программа Node запустила скрипт bash, нам нужно сделать его исполняемым. Для этого запустите следующую команду:

      • chmod u+x processNodejsImage.sh

      Это даст вашему текущему пользователю разрешение выполнять файл.

      После подготовки скрипта мы можем написать новый модуль Node.js для его выполнения. Этот скрипт будет использовать execFile() для запуска скрипта в дочернем процессе, фиксируя все ошибки и отображая все выводы в консоли.

      В своем терминале создайте новый файл JavaScript с именем getNodejsImage.js:

      Введите в текстовом редакторе следующий код:

      ~/child-processes/getNodejsImage.js

      const { execFile } = require('child_process');
      
      execFile(__dirname + '/processNodejsImage.sh', (error, stdout, stderr) => {
        if (error) {
          console.error(`error: ${error.message}`);
          return;
        }
      
        if (stderr) {
          console.error(`stderr: ${stderr}`);
          return;
        }
      
        console.log(`stdout:n${stdout}`);
      });
      

      Мы используем деструктурирование JavaScript для импорта функции execFile() из модуля child_process. Затем мы используем эту функцию, передав путь к файлу в качестве первого имени. __dirname содержит путь к каталогу модуля, в котором он написан. Node.js предоставляет переменную __dirname для модуля, когда модуль запускается. С помощью __dirname наш скрипт всегда будет находить файл processNodejsImage.sh в различных операционных системах, независимо от того, где мы запускаем getNodejsImage.js. Обратите внимание, что для настройки нашего текущего проекта getNodejsImage.js и processNodejsImage.sh должны находиться в одной папке.

      Второй аргумент — это обратный вызов с параметрами error, stdout и stderr. Как и в предыдущем примере, в котором использовалась функция exec(), мы проверяем все возможные выводы файла скрипта и записываем их в консоль.

      В текстовом редакторе сохраните этот файл и закройте редактор.

      В своем терминале используйте node для выполнения модуля:

      При запуске этого скрипта будет сгенерирован следующий вывод:

      Output

      stdout: PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNDQyLjQgMjcwLjkiPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjE4MC43IiB5MT0iODAuNyIge ...

      Обратите внимание, что в этой статье мы обрезали вывод из-за большого размера.

      Перед тем как base64 закодирует изображение, processNodejsImage.sh сначала загрузит его. Также вы можете убедиться, что изображение загружено, проверив текущий каталог.

      Выполните listFiles.js, чтобы найти обновленный список файлов в нашем каталоге:

      Скрипт отобразит содержимое, аналогичное следующему, в терминале:

      Output

      stdout: total 20K -rw-rw-r-- 1 sammy sammy 316 Jul 27 17:56 getNodejsImage.js -rw-rw-r-- 1 sammy sammy 280 Jul 27 16:35 listFiles.js -rw-rw-r-- 1 sammy sammy 5.4K Jul 27 18:01 nodejs-logo.svg -rwxrw-r-- 1 sammy sammy 129 Jul 27 17:56 processNodejsImage.sh

      Мы успешно выполнили processNodejsImage.sh в качестве дочернего процесса в Node.js с помощью функции execFile().

      Функции exec() и execFile() могут запускать команды в оболочке операционной системы в дочернем процессе Node.js. Node.js также предоставляет другой метод с аналогичными функциями, spawn(). Разница в том, что вместо получения сразу всего вывода команд оболочки мы получим его фрагментами с помощью потока. В следующем разделе мы будем использовать команду spawn() для создания дочернего процесса.

      Шаг 2 — Создание дочернего процесса с помощью spawn()

      Функция spawn() запускает команду в процессе. Эта функция возвращает данные через поток API. Поэтому для получения вывода дочернего процесса нам потребуется прослушать события потока.

      Потоки в Node.js — это экземпляры отправителей событий. Для получения дополнительной информации о прослушивании событий и основах взаимодействия с потоками, ознакомьтесь с нашим руководством Использование отправителей событий в Node.js.

      Зачастую лучше выбрать spawn() вместо exec() или execFile(), когда команда, которую вы хотите запустить, может вывести большое количество данных. С помощью буфера, как и в случае использования exec() и execFile(), все обрабатываемые данные хранятся в памяти компьютера. Для больших объемов данных это может привести к снижению производительности системы. С помощью потока данные обрабатываются и передаются небольшими фрагментами. Поэтому вы можете обрабатывать большое количество данных, не используя слишком большой объем памяти одновременно.

      Посмотрим, как можно использовать spawn() для создания дочернего процесса. Мы напишем новый модуль Node.js, который создает дочерний процесс для запуска команды find. Мы будем использовать команду find для перечисления всех файлов в текущем каталоге.

      Создайте новый файл с именем findFiles.js​​​:

      В текстовом редакторе сначала вызовем команду spawn():

      ~/child-processes/findFiles.js

      const { spawn } = require('child_process');
      
      const child = spawn('find', ['.']);
      

      Сначала мы импортировали функцию spawn() из модуля child_process. Затем мы вызвали функцию spawn() для создания дочернего процесса, который выполняет команду find. Мы удерживаем ссылку на процесс в переменной child, которую мы будем использовать для прослушивания потоковых событий.

      Первым аргументом в spawn() является команда для запуска, в данном случае find. Второй аргумент — это массив, который содержит аргументы для исполняемой команды. В этом случае мы говорим Node.js выполнить команду find с помощью аргумента ., тем самым заставляя команду находить все файлы в текущем каталоге. Аналогичной командой в терминале является find ..

      С помощью функций exec() и execFile() мы написали аргументы вместе с командой в одной строке. Однако с помощью spawn() все аргументы для команд должны быть введены в массив. Это потому, что spawn(), в отличие от exec() и execFile(), не создает новую оболочку перед запуском процесса. Чтобы команды находились в одной строке со своими аргументами, необходимо, чтобы Node.js также создал новую оболочку.

      Продолжим работу над модулем, добавив слушателей для вывода команды. Добавьте следующие выделенные строки:

      ~/child-processes/findFiles.js

      const { spawn } = require('child_process');
      
      const child = spawn('find', ['.']);
      
      child.stdout.on('data', data => {
        console.log(`stdout:n${data}`);
      });
      
      child.stderr.on('data', data => {
        console.error(`stderr: ${data}`);
      });
      

      Команды могут возвращать данные в потоке stdout или потоке stderr, поэтому вы добавили слушателей к ним обоим. Вы можете добавить слушателей, вызвав метод on() объектов всех потоков. Событие data из потоков дает нам вывод команды к этому потоку. Каждый раз, когда мы получаем данные от того или иного потока, мы записываем их в консоль.

      Затем мы будем слушать два других события: событие error, если команда не выполняется или прерывается, и событие close, если команда завершила выполнение, таким образом закрывая поток.

      В текстовом редакторе завершите модуль Node.js, написав следующие выделенные строки:

      ~/child-processes/findFiles.js

      const { spawn } = require('child_process');
      
      const child = spawn('find', ['.']);
      
      child.stdout.on('data', (data) => {
        console.log(`stdout:n${data}`);
      });
      
      child.stderr.on('data', (data) => {
        console.error(`stderr: ${data}`);
      });
      
      child.on('error', (error) => {
        console.error(`error: ${error.message}`);
      });
      
      child.on('close', (code) => {
        console.log(`child process exited with code ${code}`);
      });
      

      Для событий error и close вы назначаете слушателя непосредственно в переменной child. При прослушивании событий error, если одно из них происходит, Node.js предоставляет объект Error. В этом случае вы регистрируете свойство message ошибки.

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

      Модуль завершен. Сохраните и закройте nano с помощью CTRL+X.

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

      После выполнения вы получите следующий вывод:

      Output

      stdout: . ./findFiles.js ./listFiles.js ./nodejs-logo.svg ./processNodejsImage.sh ./getNodejsImage.js child process exited with code 0

      Мы находим список всех файлов в текущем каталоге и код выхода команды, то есть 0, так как она была запущена успешно. Хотя в нашем текущем каталоге находится небольшое количество файлов, если мы запустим этот код в домашнем каталоге, наша программа укажет каждый файл в каждой доступной пользователю папке. Поскольку потенциально вывод может оказаться большим, наиболее оптимальным будет использование функции spawn(), так как для ее потоков не требуется столько памяти, сколько может обеспечить буфер.

      Пока мы использовали функции для создания дочерних процессов для выполнения внешних команд в нашей операционной системе. Node.js также обеспечивает способ создать дочерний процесс, который выполняет другие программы Node.js. Давайте используем функцию fork() для создания дочернего процесса для модуля Node.js в следующем разделе.

      Шаг 3 — Создание дочернего процесса с помощью fork()

      Node.js предоставляет функцию fork(), вариант spawn(), для создания дочернего процесса, который также является процессом Node.js. Главным преимуществом использования fork() для создания процесса Node.js над spawn() или exec() является то, что fork() обеспечивает коммуникацию между родительским и дочерним процессом.

      С помощью fork(), помимо извлечения данных из дочернего процесса, родительский процесс может отправлять сообщения в выполняемый дочерний процесс. Аналогичным образом дочерний процесс может отправлять сообщения в родительский процесс.

      Давайте рассмотрим пример, где использование fork() для создания нового дочернего процесса Node.js может повысить производительность нашего приложения. Программы Node.js запускаются в рамках одного процесса. Поэтому ресурсоемкие задачи процессора, такие как итерация по большим циклам или синтаксический анализ крупных файлов JSON, останавливают выполнение другого кода JavaScript. Для определенных приложений этот вариант нецелесообразен. Если веб-сервер заблокирован, он не сможет обрабатывать новые входящие запросы до тех пор, пока блокирующий код не завершит выполнение.

      Давайте рассмотрим это на практике, создав веб-сервер с двумя конечными точками. Одна конечная точка будет выполнять медленные вычисления, которые блокируют процесс Node.js. Другая конечная точка вернет объект JSON hello.

      Сначала создайте новый файл с именем httpServer.js, который будет содержать код для нашего сервера HTTP:

      Для начала мы настроим сервер HTTP. Это предполагает импорт модуля http, создание функции прослушивания запроса, создание объекта сервера и прослушивание запросов на объекте сервера. Если вы хотите подробнее узнать о создании серверов HTTP в Node.js или освежить свои знания, ознакомьтесь с нашим руководством Создание веб-сервера в Node.js с помощью модуля HTTP.

      Введите в текстовом редакторе следующий код для настройки сервера HTTP:

      ~/child-processes/httpServer.js

      const http = require('http');
      
      const host="localhost";
      const port = 8000;
      
      const requestListener = function (req, res) {};
      
      const server = http.createServer(requestListener);
      server.listen(port, host, () => {
        console.log(`Server is running on http://${host}:${port}`);
      });
      

      Этот код настраивает сервер HTTP, который будет запускаться на http://localhost:8000. Он использует литералы шаблонов для динамического генерирования этого URL.

      Далее мы напишем намеренно медленную функцию, которая выполняет вычисления циклами 5 миллиардов раз. Перед функцией requestListener() добавьте следующий код:

      ~/child-processes/httpServer.js

      ...
      const port = 8000;
      
      const slowFunction = () => {
        let counter = 0;
        while (counter < 5000000000) {
          counter++;
        }
      
        return counter;
      }
      
      const requestListener = function (req, res) {};
      ...
      

      В нем используется синтаксис функции arrow для создания цикла while, который считает до 5000000000.

      Чтобы завершить этот модуль, нам нужно добавить код в функцию requestListener(). Наша функция вызовет slowFunction() на подветви и вернет небольшое сообщение JSON для другой. Добавьте в модуль следующий код:

      ~/child-processes/httpServer.js

      ...
      const requestListener = function (req, res) {
        if (req.url === '/total') {
          let slowResult = slowFunction();
          let message = `{"totalCount":${slowResult}}`;
      
          console.log('Returning /total results');
          res.setHeader('Content-Type', 'application/json');
          res.writeHead(200);
          res.end(message);
        } else if (req.url === '/hello') {
          console.log('Returning /hello results');
          res.setHeader('Content-Type', 'application/json');
          res.writeHead(200);
          res.end(`{"message":"hello"}`);
        }
      };
      ...
      

      Если пользователь достигает сервер на подветви /total, мы запустим slowFunction(). Если мы попали в подветвь /hello, мы вернем это сообщение JSON {"message":"hello"}.

      Сохраните и закройте файл, нажав CTRL+X.

      Чтобы протестировать, запустите этот модуль сервера с помощью node:

      Когда наш сервер запускается, консоль отобразит следующее:

      Output

      Server is running on http://localhost:8000

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

      • curl http://localhost:8000/total

      В другом терминале используйте curl, чтобы сделать запрос в конечную точку /hello следующим образом:

      • curl http://localhost:8000/hello

      Первый запрос вернет следующий JSON:

      Output

      {"totalCount":5000000000}

      А второй запрос вернет следующий JSON:

      Output

      {"message":"hello"}

      Запрос в /hello будет выполнен только после запроса в /total. slowFunction() заблокировала выполнение всех других кодов, так как все еще выполняется ее цикл. Проверить это можно, посмотрев на вывод сервера Node.js, который был записан в оригинальном терминале:

      Output

      Returning /total results Returning /hello results

      Для обработки блокирующего кода, пока он все еще принимает входящие запросы, мы можем переместить блокирующий код в дочерний процесс с помощью fork(). Мы переместим блокирующий код в его собственный модуль. Сервер Node.js затем создаст дочерний процесс, когда кто-то будет получать доступ к конечной точке /total, и будет слушать результаты этого дочернего процесса.

      Перепроектируйте сервер, сначала создав новый модуль с именем getCount.js, который будет содержать slowFunction():

      Теперь снова введите код slowFunction():

      ~/child-processes/getCount.js

      const slowFunction = () => {
        let counter = 0;
        while (counter < 5000000000) {
          counter++;
        }
      
        return counter;
      }
      

      Поскольку этот модуль будет дочерним процессом, созданным с помощью fork(), мы также можем добавить код для коммуникации с родительским процессом, когда slowFunction() завершит обработку. Добавьте следующий блок кода, который отправляет сообщение в родительский процесс с помощью JSON для возврата к пользователю:

      ~/child-processes/getCount.js

      const slowFunction = () => {
        let counter = 0;
        while (counter < 5000000000) {
          counter++;
        }
      
        return counter;
      }
      
      process.on('message', (message) => {
        if (message == 'START') {
          console.log('Child process received START message');
          let slowResult = slowFunction();
          let message = `{"totalCount":${slowResult}}`;
          process.send(message);
        }
      });
      

      Давайте разберем этот блок кода. Сообщения между родительским и дочерним процессами, созданные fork(), доступны через глобальный объект process Node.js. Мы добавим слушателя в переменную process для поиска событий message. Получив событие message, мы проверим, является ли оно событием START. Наш код сервера будет отправлять событие START, когда кто-то будет получать доступ к конечной точке /total. После получения этого события мы запускаем slowFunction() и создаем строку JSON с результатом функции. Мы используем process.send() для отправки сообщения в родительский процесс.

      Сохраните и закройте getCount.js, введя CTRL+X в nano.

      Теперь давайте изменим файл httpServer.js, чтобы вместо вызова slowFunction() он создавал дочерний процесс, который выполняет getCount.js.

      Откройте повторно httpServer.js с помощью nano:

      Сначала импортируйте функцию fork() из модуля child_process:

      ~/child-processes/httpServer.js

      const http = require('http');
      const { fork } = require('child_process');
      ...
      

      Далее мы удалим slowFunction() из этого модуля и изменим функцию requestListener() для создания дочернего процесса. Измените код в вашем файле, чтобы он выглядел следующим образом:

      ~/child-processes/httpServer.js

      ...
      const port = 8000;
      
      const requestListener = function (req, res) {
        if (req.url === '/total') {
          const child = fork(__dirname + '/getCount');
      
          child.on('message', (message) => {
            console.log('Returning /total results');
            res.setHeader('Content-Type', 'application/json');
            res.writeHead(200);
            res.end(message);
          });
      
          child.send('START');
        } else if (req.url === '/hello') {
          console.log('Returning /hello results');
          res.setHeader('Content-Type', 'application/json');
          res.writeHead(200);
          res.end(`{"message":"hello"}`);
        }
      };
      ...
      

      Теперь, когда кто-то попадет в конечную точку /total, мы создаем новый дочерний процесс с помощью fork(). Аргумент fork() — это путь к модулю Node.js. В данном случае это файл getCount.js в нашем текущем каталоге, который мы получаем от __dirname. Ссылка на этот дочерний процесс хранится в переменной child.

      Затем мы добавим слушателя в объект child. Этот слушатель фиксирует все сообщения, которые нам дает дочерний процесс. В данном случае getCount.js вернет строку JSON с общим числом, которое подсчитал цикл while. Когда мы получим это сообщение, мы отправим JSON пользователю.

      Мы используем функцию send() переменной child для передачи ей сообщения. Эта программа отправляет сообщение START, которое начинает выполнение slowFunction() в дочернем процессе.

      Сохраните и закройте nano, нажав CTRL+X.

      Чтобы протестировать усовершенствование с помощью fork(), выполненной на сервере HTTP, начните с выполнения файла httpServer.js с помощью node:

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

      Output

      Server is running on http://localhost:8000

      Чтобы протестировать сервер, нам понадобится два дополнительных терминала, как и в первый раз. Вы можете повторно их использовать, если они все еще открыты.

      В первом терминале используйте команду curl, чтобы сделать запрос в конечную точку /total, которой потребуется какое-то время для вычислений:

      • curl http://localhost:8000/total

      В другом терминале используйте curl, чтобы сделать запрос в конечную точку /hello, которая отвечает быстро:

      • curl http://localhost:8000/hello

      Первый запрос вернет следующий JSON:

      Output

      {"totalCount":5000000000}

      Тогда как второй запрос вернет следующий JSON:

      Output

      {"message":"hello"}

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

      Output

      Child process received START message Returning /hello results Returning /total results

      Эти журналы показывают, что запрос в конечную точку /hello был запущен после создания дочернего процесса, но до того как дочерний процесс завершил свою задачу.

      Поскольку мы переместили блокирующий код в дочерний процесс с помощью fork(), сервер все еще мог реагировать на другие запросы и выполнять другой код JavaScript. Из-за передающей способности сообщения функции fork() мы можем контролировать, когда начинает активность дочерний процесс, и мы можем вернуть данные из дочернего процесса в родительский процесс.

      Заключение

      В этой статье вы использовали различные функции для создания дочернего процесса в Node.js. Сначала вы создали дочерние процессы с помощью exec() для запуска команд оболочки из кода Node.js. Затем вы запустили исполняемый файл с помощью функции execFile(). Вы рассмотрели функцию spawn(), которая также может запускать команды, но возвращает данные через поток и не запускает оболочку, как exec() и execFile(). Наконец, вы использовали функцию fork() для обеспечения двусторонней связи между родительским и дочерним процессом.

      Дополнительную информацию о модуле child_process можно найти в документации Node.js. Если хотите продолжить изучение Node.js, то можете вернуться к серии Программирование на Node.js или ознакомиться с проектами и конфигурациями на нашей странице разделов Node.



      Source link

      Запуск нескольких версий PHP на одном сервере с использованием Apache и PHP-FPM в CentOS 7


      Автор выбрал COVID-19 Relief Fund для получения пожертвования в рамках программы Write for DOnations.

      Введение

      Веб-сервер Apache использует виртуальные хосты для управления несколькими доменами в одной системе. PHP-FPM использует демона для управления несколькими версиями PHP в одной системе. Вы можете использовать Apache и PHP-FPM для одновременного хостинга на одном сервере нескольких веб-приложений PHP на основе разных версий PHP. Эта возможность полезна, поскольку разным приложениям могут требоваться разные версии PHP, но некоторые серверные комплексы, в том числе комплекс LAMP в стандартной конфигурации, могут работать только с одной версией. Сочетание Apache с PHP-FPM более экономично по сравнению с хостингом каждого приложения на отдельном экземпляре сервера.

      Также PHP-FPM предлагает разные варианты конфигурации для регистрации данных stderr и stdout, аварийной перезагрузки и адаптивного создания процессов, что полезно для сайтов с высокой нагрузкой. Использование Apache с PHP-FPM — один из лучших вариантов хостинга приложений PHP, особенно с точки зрения производительности.

      В этом обучающем руководстве мы настроим два сайта PHP для работы на одном экземпляре сервера. Каждый сайт будет использовать собственный домен, и на каждом домене будет использоваться собственная версия PHP. Первый сайт site1.your_domain развернет PHP 7.0. Второй сайт site2.your_domain развернет PHP 7.2.

      Предварительные требования

      • Один сервер CentOS 7 с не менее чем 1 ГБ оперативной памяти, настроенный согласно руководству Начальная настройка сервера CentOS 7, с пользователем non-root user с привилегиями sudo и брандмауэром.
      • Веб-сервер Apache, установленный и настроенный в соответствии с указаниями руководства Установка веб-сервера Apache в CentOS 7​​​.
      • Доменное имя, настроенное так, чтобы указывать на ваш сервер CentOS 7. Информацию о том, как сделать так, чтобы домены указывали на дроплеты DigitalOcean, можно найти в руководстве Создание указаний на серверы имен DigitalOcean из общих реестров доменов. Для целей настоящего обучающего модуля мы используем два субдомена, каждый из которых указан с записью A в наших настройках DNS: site1.your_domain и site2.your_domain.

      Шаг 1 — Установка PHP версий 7.0 и 7.2 с помощью PHP-FPM

      После выполнения предварительных требований мы установим версии PHP 7.0 и 7.2. Репозиторий SCL (Software Collections) содержит множество версий стеков PHP для систем CentOS 7. Если вам нужна абсолютно новая версия PHP, которой нет на SCL, проверьте remi PPA (персональные архивы пакетов).

      Начнем с установки репозитория SCL в вашу систему:

      • sudo yum install centos-release-scl -y

      Сначала узнаем, какие версии PHP 7 доступны в SCL:

      • sudo yum list rh-php7[0-9].x86_64

      Вывод будет выглядеть следующим образом:

      Output

      Available Packages rh-php70.x86_64 2.3-1.el7 centos-sclo-rh rh-php71.x86_64 1-1.el7 centos-sclo-rh rh-php72.x86_64 1-2.el7 centos-sclo-rh rh-php73.x86_64 1-1.el7 centos-sclo-rh

      Вы заметите, что доступна также новейшая версия, PHP 7.3. Но в наших примерах мы установим версии 7.0 и 7.2.

      Начнем с более ранней версии. Установите rh-php70 и rh-php70-php-fpm​​​:

      • sudo yum install rh-php70 rh-php70-php-fpm -y
      • rh-php70 — это метапакет, который запускает приложения PHP.
      • rh-php70-php-fpm​​​ предоставляет интерпретатор Fast Process Manager, который работает как демон и принимает запросы Fast/CGI.

      Повторите процедуру для PHP версии 7.2. Установите rh-php72 и rh-php72-php-fpm​​​.

      • sudo yum install rh-php72 rh-php72-php-fpm -y

      Далее запустите следующие команды для начала использования обеих коллекций Software Collections:

      • sudo scl enable rh-php70 bash
      • sudo scl enable rh-php72 bash

      По умолчанию обе версии PHP прослушивают порт 9000. Но в этом обучающем руководстве мы хотим запустить две версии одновременно. Поэтому назначим два новых порта:

      Для этого откройте /etc/opt/rh/rh-php70/php-fpm.d/www.conf в предпочитаемом текстовом редакторе и измените каждое представление 9000 на 9002. Затем сохраните и закройте файл и повторите процедуру для /etc/opt/rh/rh-php72/php-fpm.d/www.conf, только сейчас замените 9000 на 9003. Вы можете использовать эти две команды sed​​​ поочередно, чтобы выполнить замены:

      • sudo sed -i 's/:9000/:9002/' /etc/opt/rh/rh-php70/php-fpm.d/www.conf
      • sudo sed -i 's/:9000/:9003/' /etc/opt/rh/rh-php72/php-fpm.d/www.conf

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

      SELinux — это сокращение от Security Enhanced Linux, которое активируется по умолчанию на CentOS 7. Необходимо добавить новые порты 9002 и 9003 в вашу базу данных SELinux и назначить их для ваших служб httpd, иначе ваши приложения не будут запускаться. Для выполнения этой задачи используйте команду semanage:

      • sudo semanage port -a -t http_port_t -p tcp 9002
      • sudo semanage port -a -t http_port_t -p tcp 9003

      Флаг -a указывает на то, что вы добавляете объект в базу данных. Флаг -t указывает тип объекта, которым в данном случае является http_port_t​​​. А флаг -p определяет протокол tcp​​​. Дополнительную информацию о SELinux и команде semanage можно найти в этом обучающем руководстве или обратившись к официальной документации SELinux.

      Теперь вы готовы запустить и активировать ваши службы PHP. Начнем со службы rh-php70-php-fpm и активируем ее запуск во время загрузки:

      • sudo systemctl start rh-php70-php-fpm
      • sudo systemctl enable rh-php70-php-fpm

      Затем проверьте статус службы rh-php70-php-fpm:

      • sudo systemctl status rh-php70-php-fpm

      Вывод будет выглядеть следующим образом:

      Output

      ● rh-php70-php-fpm.service - The PHP FastCGI Process Manager Loaded: loaded (/usr/lib/systemd/system/rh-php70-php-fpm.service; enabled; vendor preset: disabled) Active: active (running) since Sat 2020-04-18 04:49:59 UTC; 1min 6s ago Main PID: 1852 (php-fpm) Status: "Processes active: 0, idle: 5, Requests: 0, slow: 0, Traffic: 0req/sec" CGroup: /system.slice/rh-php70-php-fpm.service ├─1852 php-fpm: master process (/etc/opt/rh/rh-php70/php-fpm.conf) ├─1853 php-fpm: pool www ├─1854 php-fpm: pool www ├─1855 php-fpm: pool www ├─1856 php-fpm: pool www └─1857 php-fpm: pool www Apr 18 04:49:59 centos-s-1vcpu-1gb-blr1-01 systemd[1]: Starting The PHP FastCGI Process Manager... Apr 18 04:49:59 centos-s-1vcpu-1gb-blr1-01 systemd[1]: Started The PHP FastCGI Process Manager.

      Повторите эту процедуру, запустите службу rh-php72-php-fpm и активируйте ее запуск во время загрузки:

      • sudo systemctl start rh-php72-php-fpm
      • sudo systemctl enable rh-php72-php-fpm

      Затем проверьте статус службы rh-php72-php-fpm:

      • sudo systemctl status rh-php72-php-fpm

      Другой вывод будет выглядеть следующим образом:

      Output

      ● rh-php72-php-fpm.service - The PHP FastCGI Process Manager Loaded: loaded (/usr/lib/systemd/system/rh-php72-php-fpm.service; enabled; vendor preset: disabled) Active: active (running) since Sat 2020-04-18 04:50:04 UTC; 1min 59s ago Main PID: 1876 (php-fpm) Status: "Processes active: 0, idle: 5, Requests: 0, slow: 0, Traffic: 0req/sec" CGroup: /system.slice/rh-php72-php-fpm.service ├─1876 php-fpm: master process (/etc/opt/rh/rh-php72/php-fpm.conf) ├─1877 php-fpm: pool www ├─1878 php-fpm: pool www ├─1879 php-fpm: pool www ├─1880 php-fpm: pool www └─1881 php-fpm: pool www Apr 18 04:50:04 centos-s-1vcpu-1gb-blr1-01 systemd[1]: Starting The PHP FastCGI Process Manager... Apr 18 04:50:04 centos-s-1vcpu-1gb-blr1-01 systemd[1]: Started The PHP FastCGI Process Manager.

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

      Шаг 2 — Создание структур каталогов для обоих сайтов

      В этом разделе мы создадим корневой каталог документов и страницу индекса для каждого из двух сайтов.

      Вначале создайте корневые каталоги документов для site1.your_domain и site2.your_domain:

      • sudo mkdir /var/www/site1.your_domain
      • sudo mkdir /var/www/site2.your_domain

      По умолчанию веб-сервер Apache работает как пользователь apache и группа apache. Поэтому /var/www/ и все его файлы и подкаталоги также должны принадлежать им. Запустите следующие команды для проверки правильности владения и разрешений корневых каталогов вашего веб-сайта:

      • sudo chown -R apache:apache /var/www/site1.your_domain
      • sudo chown -R apache:apache /var/www/site2.your_domain
      • sudo chmod -R 755 /var/www/site1.your_domain
      • sudo chmod -R 755 /var/www/site2.your_domain

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

      Далее вы создадите файл info.php в корневом каталоге каждого сайта. В нем будет отображаться информация о версии PHP для каждого сайта. Начнем с site1:

      • sudo vi /var/www/site1.your_domain/info.php

      Добавьте следующую строку:

      /var/www/site1.your_domain/info.php

      <?php phpinfo(); ?>
      

      Сохраните и закройте файл. Скопируйте созданный файл info.php в site2:

      • sudo cp /var/www/site1.your_domain/info.php /var/www/site2.your_domain/info.php

      Теперь на вашем веб-сервере имеются корневые каталоги документов, которые требуются каждому сайту для предоставления данных посетителям. Далее мы настроим веб-сервер Apache для работы с двумя разными версиями PHP.

      Шаг 3 — Настройка Apache для обоих сайтов

      В этом разделе мы создадим два файла конфигурации виртуального хоста. Это позволит двум нашим сайтам одновременно работать с двумя разными версиями PHP.

      Для обслуживания этого контента Apache необходимо создать файл виртуального хоста с правильными директивами. Вы создадите два новых файла конфигурации виртуального хоста внутри каталога /etc/httpd/conf.d/.

      Вначале создайте новый файл конфигурации виртуального хоста для сайта site1.your_domain. Здесь вы предписываете Apache использовать для рендеринга содержимого PHP 7.0:

      • sudo vi /etc/httpd/conf.d/site1.your_domain.conf

      Добавьте в файл следующее: Убедитесь, что путь к каталогу сайта, имя сервера, порт и версия PHP соответствуют вашей настройке:

      /etc/httpd/conf.d/site1.your_domain.conf

      
      <VirtualHost *:80>
           ServerAdmin admin@site1.your_domain
           ServerName site1.your_domain
           DocumentRoot /var/www/site1.your_domain
           DirectoryIndex info.php
           SetHandler "proxy:fcgi://127.0.0.1:9002
           ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"
           AddHandler php70-fcgi .php
           Action php70-fcgi /cgi-bin/php70.fcgi
           ErrorLog /var/log/httpd/site1.your_domain_error.log
           CustomLog /var/log/httpd/site1.your_domain_access.log combined
      </VirtualHost>
      

      Для DocumentRoot вы указываете путь корневого каталога вашего веб-сайта. Для ServerAdmin вы добавляете адрес электронной почты, к которому имеет доступ администратор сайта your_domain. Для ServerName вы добавляете url для вашего первого субдомена. Для SetHandler вы указываете порт 9002. Остальные директивы также настраивают вашу службу для развертывания PHP 7.0.

      Сохраните и закройте файл.

      Теперь создайте новый файл конфигурации виртуального хоста для сайта site2.your_domain. Для этого субдомена мы будем развертывать PHP 7.2:

      • sudo vi /etc/httpd/conf.d/site2.your_domain.conf

      Добавьте в файл следующее: Убедитесь, что путь к каталогу сайта, имя сервера, порт и версия PHP соответствуют уникальным параметрам вашей системы:

      /etc/httpd/conf.d/site2.your_domain.conf

      <VirtualHost *:80>
           ServerAdmin admin@site2.your_domain
           ServerName site2.your_domain
           DocumentRoot /var/www/site2.your_domain
           DirectoryIndex info.php
           SetHandler "proxy:fcgi://127.0.0.1:9003
           ScriptAlias /cgi-bin/ "/var/www/cgi-bin/"
           AddHandler php72-fcgi .php
           Action php72-fcgi /cgi-bin/php72.fcgi
           ErrorLog /var/log/httpd/site2.your_domain_error.log
           CustomLog /var/log/httpd/site2.your_domain_access.log combined
      </VirtualHost>
      

      Сохраните файл и закройте его после завершения. Проверьте файл конфигурации Apache на наличие синтаксических ошибок:

      • sudo apachectl configtest

      Вы увидите вывод с текстом Syntax OK:

      Output

      Перезапустите службу Apache, чтобы применить изменения:

      • sudo systemctl restart httpd

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

      Шаг 4 — Тестирование сайтов

      Мы настроили два сайта для работы с двумя разными версиями PHP. Теперь проверим результаты.

      Откройте в браузере сайты http://site1.your_domain и http://site2.your_domain. Вы увидите две страницы, выглядящие следующим образом:

      Информационная страницаPHP 7.0 Информационная страница PHP 7.2

      Обратите внимание на заголовки. На первой странице указано, что на сайте site1.your_domain развернута версия PHP 7.0. На второй странице указано, что на сайте site2.your_domain развернута версия PHP 7.2.

      Мы протестировали сайты и теперь можем удалить файлы info.php. Эти файлы представляют собой угрозу безопасности, поскольку они содержат важную информацию о вашем сервере и при этом доступны неуполномоченным пользователям. Удалите файлы:

      • sudo rm -rf /var/www/site1.your_domain/info.php
      • sudo rm -rf /var/www/site2.your_domain/info.php

      Теперь у вас имеется один сервер CentOS 7, обслуживающий два сайта с двумя разными версиями PHP. Однако PHP-FPM можно применять и для других целей.

      Заключение

      Мы объединили виртуальные хосты и PHP-FPM для обслуживания нескольких сайтов и нескольких версий PHP на одном сервере. Количество сайтов PHP и версий PHP, которые может обслуживать ваш сервер Apache, зависит исключительно от вычислительной мощности сервера.

      Теперь вы можете рассмотреть возможность изучения более расширенных функций PHP-FPM, например порождение процесса или правил регистрации sdtout и stderr​​​​​. Или же теперь вы можете защитить свои веб-сайты. Для этого используйте наш обучающий модуль по защите сайтов с помощью бесплатных сертификатов TLS/SSL от Let’s Encrypt.



      Source link

      Запуск нескольких версий PHP на одном сервере с использованием Apache и PHP-FPM в Debian 10


      Автор выбрал COVID-19 Relief Fund для получения пожертвования в рамках программы Write for DOnations.

      Введение

      Веб-сервер Apache использует виртуальные хосты для управления несколькими доменами в одной системе. PHP-FPM использует демона для управления несколькими версиями PHP в одной системе. Вы можете использовать Apache и PHP-FPM для одновременного хостинга на одном сервере нескольких веб-приложений PHP на основе разных версий PHP. Эта возможность полезна, поскольку разным приложениям могут требоваться разные версии PHP, но некоторые серверные комплексы, в том числе стек LAMP в стандартной конфигурации, могут работать только с одной версией. Сочетание Apache с PHP-FPM более экономично по сравнению с хостингом каждого приложения на отдельном экземпляре сервера.

      Также PHP-FPM предлагает разные варианты конфигурации для регистрации данных stderr и stdout, аварийной перезагрузки и адаптивного создания процессов, что полезно для сайтов с высокой нагрузкой. Использование Apache с PHP-FPM — один из лучших вариантов хостинга приложений PHP, особенно с точки зрения производительности.

      В этом обучающем руководстве мы настроим два сайта PHP для работы на одном экземпляре сервера. Каждый сайт будет использовать собственный домен, и на каждом домене будет использоваться собственная версия PHP. Первый сайт site1.your_domain развернет PHP 7.0. Второй сайт site2.your_domain развернет PHP 7.2.

      Предварительные требования

      • Один сервер Debian 10 с не менее чем 1 ГБ оперативной памяти, настроенный согласно руководству Начальная настройка сервера Debian 10, с пользователем non-root user с привилегиями sudo и брандмауэром.
      • Веб-сервер Apache, установленный и настроенный в соответствии с указаниями руководства Установка веб-сервера Apache в Debian 10.
      • Доменное имя, настроенное так, чтобы указывать на ваш сервер Debian 10. Информацию о том, как сделать так, чтобы домены указывали на дроплеты DigitalOcean, можно найти в руководстве Создание указаний на серверы имен DigitalOcean из общих реестров доменов. Для целей настоящего обучающего руководства мы используем два субдомена, каждый из которых указан с записью A в наших настройках DNS: site1.your_domain и site2.your_domain.

      Шаг 1 — Установка PHP версий 7.0 и 7.2 с помощью PHP-FPM

      Выполнив предварительные требования, вы можете установить PHP версий 7.0 и 7.2, а также PHP-FPM и некоторые дополнительные расширения. Для этого предварительно необходимо добавить в систему репозиторий sury php.

      Вначале установите требуемые пакеты, в том числе curl, wget и gnupg2:

      • sudo apt-get install curl wget gnupg2 ca-certificates lsb-release apt-transport-https -y

      Вышеуказанные пакеты позволяют получить безопасный доступ к репозиторию sury php. sury php — это сторонний репозиторий или PPA (архив персональных пакетов). Он предоставляет PHP 7.4, 7.3, 7.2, 7.1 и 7.0 для операционной системы Debian. Также он включает более актуальные версии PHP, чем содержащиеся в официальных репозиториях Debian 10, и позволяет устанавливать несколько версий PHP в одной системе.

      Затем импортируйте ключ пакета:

      • wget https://packages.sury.org/php/apt.gpg
      • sudo apt-key add apt.gpg

      Теперь добавьте в систему репозиторий sury php:

      • echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/php7.list

      Обновите репозиторий:

      Установите php7.0, php7.0-fpm, php7.0-mysql, libapache2-mod-php7.0 и libapache2-mod-fcgid с помощью следующих команд:

      • sudo apt-get install php7.0 php7.0-fpm php7.0-mysql libapache2-mod-php7.0 libapache2-mod-fcgid -y
      • php7.0 — это метапакет, который можно использовать для запуска приложений PHP.
      • php7.0-fpm предоставляет интерпретатор Fast Process Manager, который работает как демон и принимает запросы Fast/CGI.
      • php7.0-mysql связывает PHP с базой данных MySQL.
      • libapahce2-mod-php7.0 предоставляет модуль PHP для веб-сервера Apache.
      • libapache2-mod-fcgid содержит mod_fcgid, запускающий несколько экземпляров программы CGI для обработки одновременных запросов.

      Повторите процедуру для PHP версии 7.2. Установите php7.2, php7.2-fpm, php7.2-mysql и libapache2-mod-php7.2.

      • sudo apt-get install php7.2 php7.2-fpm php7.2-mysql libapache2-mod-php7.2 -y

      После установки обеих версий PHP запустите службу php7.0-fpm:

      • sudo systemctl start php7.0-fpm

      Затем проверьте статус службы php7.0-fpm:

      • sudo systemctl status php7.0-fpm

      Вывод должен выглядеть так:

      Output

      ● php7.0-fpm.service - The PHP 7.0 FastCGI Process Manager Loaded: loaded (/lib/systemd/system/php7.0-fpm.service; enabled; vendor preset: enabled) Active: active (running) since Sat 2020-04-04 08:51:47 UTC; 1min 17s ago Docs: man:php-fpm7.0(8) Main PID: 13016 (php-fpm7.0) Status: "Processes active: 0, idle: 2, Requests: 0, slow: 0, Traffic: 0req/sec" Tasks: 3 (limit: 1149) Memory: 19.1M CGroup: /system.slice/php7.0-fpm.service ├─13016 php-fpm: master process (/etc/php/7.0/fpm/php-fpm.conf) ├─13017 php-fpm: pool www └─13018 php-fpm: pool www Apr 04 08:51:47 debian10 systemd[1]: Starting The PHP 7.0 FastCGI Process Manager... Apr 04 08:51:47 debian10 systemd[1]: Started The PHP 7.0 FastCGI Process Manager.

      Повторите процедуру и запустите службу php7.2-fpm:

      • sudo systemctl start php7.2-fpm

      Затем проверьте статус службы php7.2-fpm:

      • sudo systemctl status php7.2-fpm

      Вывод должен выглядеть так:

      Output

      ● php7.2-fpm.service - The PHP 7.2 FastCGI Process Manager Loaded: loaded (/lib/systemd/system/php7.2-fpm.service; enabled; vendor preset: enabled) Active: active (running) since Sat 2020-04-04 08:52:52 UTC; 1min 32s ago Docs: man:php-fpm7.2(8) Process: 22207 ExecStartPost=/usr/lib/php/php-fpm-socket-helper install /run/php/php-fpm.sock /etc/php/7.2/fpm/pool.d/www.conf 72 (code=exite Main PID: 22204 (php-fpm7.2) Status: "Processes active: 0, idle: 2, Requests: 0, slow: 0, Traffic: 0req/sec" Tasks: 3 (limit: 1149) Memory: 12.0M CGroup: /system.slice/php7.2-fpm.service ├─22204 php-fpm: master process (/etc/php/7.2/fpm/php-fpm.conf) ├─22205 php-fpm: pool www └─22206 php-fpm: pool www Apr 04 08:52:52 debian10 systemd[1]: Starting The PHP 7.2 FastCGI Process Manager... Apr 04 08:52:52 debian10 systemd[1]: Started The PHP 7.2 FastCGI Process Manager.

      В заключение необходимо активировать несколько модулей, чтобы служба Apache2 могла работать с несколькими версиями PHP:

      • sudo a2enmod actions fcgid alias proxy_fcgi
      • actions используется для выполнения скриптов CGI на основе типа носителя или метода запроса.

      • fcgid — это высокопроизводительная альтернатива mod_cgi, запускающая достаточное количество экземпляров программы CGI для одновременной обработки запросов.

      • alias позволяет создавать схемы разных деталей файловой системы хоста в дереве документов и для целей переадресации URL.

      • proxy_fcgi позволяет Apache перенаправлять запросы PHP-FPM.

      Перезапустите службу Apache, чтобы применить изменения:

      • sudo systemctl restart apache2

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

      Шаг 2 — Создание структур директорий для обоих сайтов

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

      Вначале создайте корневые директории документов для site1.your_domain и site2.your_domain:

      • sudo mkdir /var/www/site1.your_domain
      • sudo mkdir /var/www/site2.your_domain

      По умолчанию веб-сервер Apache работает как пользователь www-data и группа www-data. Чтобы убедиться в правильности структуры владения и разрешений для корневых директорий вашего сайта, используйте следующие команды:

      • sudo chown -R www-data:www-data /var/www/site1.your_domain
      • sudo chown -R www-data:www-data /var/www/site2.your_domain
      • sudo chmod -R 755 /var/www/site1.your_domain
      • sudo chmod -R 755 /var/www/site2.your_domain

      Далее вы создадите файл info.php в корневой директории каждого сайта. В нем будет отображаться информация о версии PHP для каждого сайта. Начнем с site1:

      • sudo nano /var/www/site1.your_domain/info.php

      Добавьте следующую строку:

      /var/www/site1.your_domain/info.php

      <?php phpinfo(); ?>
      

      Сохраните и закройте файл. Скопируйте созданный файл info.php в site2:

      • sudo cp /var/www/site1.your_domain/info.php /var/www/site2.your_domain/info.php

      Теперь на вашем веб-сервере должны иметься корневые директории документов, которые требуются каждому сайту для предоставления данных посетителям. Далее мы настроим веб-сервер Apache для работы с двумя разными версиями PHP.

      Шаг 3 — Настройка Apache для обоих сайтов

      В этом разделе мы создадим два файла конфигурации виртуального хоста. Это позволит двум нашим сайтам одновременно работать с двумя разными версиями PHP.

      Для обслуживания этого контента Apache необходимо создать файл виртуального хоста с правильными директивами. Вместо изменения файла конфигурации по умолчанию /etc/apache2/sites-available/000-default.conf мы создадим два новых файла в директории /etc/apache2/sites-available/.

      Вначале создайте новый файл конфигурации виртуального хоста для сайта site1.your_domain. Здесь вы предписываете Apache использовать для рендеринга содержимого php7.0:

      • sudo nano /etc/apache2/sites-available/site1.your_domain.conf

      Добавьте в файл следующее. Убедитесь, что путь к директории сайта, имя сервера и версия PHP соответствуют вашей системе:

      /etc/apache2/sites-available/site1.your_domain.conf

      
      <VirtualHost *:80>
           ServerAdmin admin@site1.your_domain
           ServerName site1.your_domain
           DocumentRoot /var/www/site1.your_domain
           DirectoryIndex info.php
      
           <Directory /var/www/site1.your_domain>
              Options Indexes FollowSymLinks MultiViews
              AllowOverride All
              Order allow,deny
              allow from all
           </Directory>
      
          <FilesMatch .php$>
            # For Apache version 2.4.10 and above, use SetHandler to run PHP as a fastCGI process server
            SetHandler "proxy:unix:/run/php/php7.0-fpm.sock|fcgi://localhost"
          </FilesMatch>
      
           ErrorLog ${APACHE_LOG_DIR}/site1.your_domain_error.log
           CustomLog ${APACHE_LOG_DIR}/site1.your_domain_access.log combined
      </VirtualHost>
      

      В этом файле вы изменили директорию на DocumentRoot, а ServerAdmin на адрес электронной почты, доступный администратору сайта your_domain. Также вы изменили параметр ServerName, устанавливающий базовый домен для этой конфигурации виртуального хоста, и добавили директиву SetHandler для запуска PHP как сервера процессов fastCGI.

      Сохраните и закройте файл.

      Теперь создайте новый файл конфигурации виртуального хоста для сайта site2.your_domain. Для этого субдомена мы будем развертывать php7.2:

      • sudo nano /etc/apache2/sites-available/site2.your_domain.conf

      Добавьте в файл следующее. Убедитесь, что путь к директории сайта, имя сервера и версия PHP соответствуют уникальным параметрам вашей системы:

      /etc/apache2/sites-available/site2.your_domain.conf

      <VirtualHost *:80>
           ServerAdmin admin@site2.your_domain
           ServerName site2.your_domain
           DocumentRoot /var/www/site2.your_domain
           DirectoryIndex info.php  
      
           <Directory /var/www/site2.your_domain>
              Options Indexes FollowSymLinks MultiViews
              AllowOverride All
              Order allow,deny
              allow from all
           </Directory>
      
          <FilesMatch .php$>
            # For Apache version 2.4.10 and above, use SetHandler to run PHP as a fastCGI process server
            SetHandler "proxy:unix:/run/php/php7.2-fpm.sock|fcgi://localhost"
          </FilesMatch>
      
           ErrorLog ${APACHE_LOG_DIR}/site2.your_domain_error.log
           CustomLog ${APACHE_LOG_DIR}/site2.your_domain_access.log combined
      </VirtualHost>
      

      Сохраните файл и закройте его после завершения. Проверьте файл конфигурации Apache на наличие синтаксических ошибок:

      • sudo apachectl configtest

      Вывод должен выглядеть так:

      Output

      Syntax OK

      Активируйте оба файла конфигурации виртуального хоста:

      • sudo a2ensite site1.your_domain
      • sudo a2ensite site2.your_domain

      Отключите сайт по умолчанию, поскольку он не потребуется:

      • sudo a2dissite 000-default.conf

      Перезапустите службу Apache, чтобы применить изменения:

      • sudo systemctl restart apache2

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

      Шаг 4 — Тестирование сайтов

      Мы настроили два сайта для работы с двумя разными версиями PHP. Теперь проверим результаты.

      Откройте в браузере сайты http://site1.your_domain и http://site2.your_domain. Вы увидите две страницы, выглядящие следующим образом:

      Информационная страница PHP 7.0Информационная страница PHP 7.2

      Обратите внимание на заголовки. На первой странице указано, что на сайте site1.your_domain развернута версия PHP 7.0. На второй странице указано, что на сайте site2.your_domain развернута версия PHP 7.2.

      Мы протестировали сайты и теперь можем удалить файлы info.php. Эти файлы представляют собой угрозу безопасности, поскольку они содержат важную информацию о вашем сервере и при этом доступны неуполномоченным пользователям. Чтобы удалить оба файла, запустите следующие команды:

      • sudo rm -rf /var/www/site1.your_domain/info.php
      • sudo rm -rf /var/www/site2.your_domain/info.php

      Теперь у вас имеется один сервер Debian 10, обслуживающий два сайта с разными версиями PHP. Однако PHP-FPM можно применять и для других целей.

      Заключение

      Мы объединили виртуальные хосты и PHP-FPM для обслуживания нескольких сайтов и нескольких версий PHP на одном сервере. Количество сайтов PHP и версий PHP, которые может обслуживать ваш сервер Apache, зависит исключительно от вычислительной мощности вашего экземпляра.

      Теперь вы можете начать изучение более сложных функций PHP-FPM, таких как процесс адаптивного создания или функции регистрации sdtout и stderr. Также вы можете заняться защитой своих сайтов. Для этого используйте наш обучающий модуль по защите сайтов с помощью бесплатных сертификатов TLS/SSL от Let’s Encrypt.



      Source link