One place for hosting & domains

      Nodejs

      Como configurar um aplicativo Node.js para produção no Ubuntu 20.04


      Introdução

      O Node.js é um ambiente de execução de código aberto do JavaScript para construção de aplicativos de rede e do servidor. A plataforma executa nos sistemas operacionais Linux, macOS, FreeBSD e Windows. Embora você possa executar aplicativos Node.js na linha de comando, este tutorial se concentrará em executá-los como um serviço. Isso significa que eles irão reiniciar após reinicialização ou falha, sendo seguros para utilização em um ambiente de produção.

      Neste tutorial, você irá configurar um ambiente Node.js pronto para produção em um único servidor Ubuntu 20.04. Este servidor executará um aplicativo Node.js gerenciado pelo PM2 e fornecerá aos usuários acesso seguro ao aplicativo através de um proxy reverso Nginx. O servidor Nginx oferecerá HTTPS usando um certificado gratuito fornecido pelo Let’s Encrypt.

      Pré-requisitos

      Este guia supõe que você tenha o seguinte:

      Quando você tiver cumprido com os pré-requisitos, você terá um servidor atendendo a página do espaço reservado padrão do seu domínio em https://example.com/.

      Passo 1 — Instalando o Node.js

      Vamos começar instalando a versão mais recente do Node.js com LTS, ou Long-Term Support (Suporte de longo prazo), usando os arquivos do pacote NodeSource.

      Primeiramente, instale o NodeSource PPA para ter acesso ao seu conteúdo. Certifique-se de que você esteja em seu diretório base, e use o curl para recuperar o script de instalação para a versão LTS mais recente do Node.js dos seus arquivos.

      • cd ~
      • curl -sL https://deb.nodesource.com/setup_14.x -o nodesource_setup.sh

      É possível verificar o conteúdo deste script com o nano ou seu editor de texto preferido:

      Quando terminar de inspecionar o script, execute-o sob o sudo:

      • sudo bash nodesource_setup.sh

      O PPA será adicionado à sua configuração e seu cache de pacotes local será atualizado automaticamente. Após executar o script de configuração do Nodesource, você pode instalar o pacote do Node.js:

      Para verificar qual versão do Node.js você tem instalada após esses passos iniciais, digite:

      Output

      v14.4.0

      Nota: ao instalar a partir do NodeSource com PPA, o arquivo executável Node.js é chamado de nodejs e não node.

      O pacote nodejs contém o binário do nodejs assim como o npm, um gerenciador de pacotes para módulos Node, então não é necessário instalar o npm separadamente.

      O npm usa um arquivo de configuração no seu diretório home para controlar as atualizações. Ele será criado na primeira vez que você executar o npm. Execute este comando para verificar se o npm está instalado e crie o arquivo de configuração:

      Output

      6.14.5

      Para que alguns pacotes npm possam funcionar (os que requerem compilar o código da fonte, por exemplo), será necessário instalar o pacote build-essential:

      • sudo apt install build-essential

      Agora, você tem as ferramentas necessárias para trabalhar com os pacotes npm que requerem a compilação do código da fonte.

      Com o ambiente de execução Node.js instalado, vamos seguir em frente para escrever um aplicativo Node.js.

      Passo 2 — Criando um aplicativo Node.js

      Vamos escrever um aplicativo Hello World que retorna “Hello World” a qualquer pedido de HTTP. Este aplicativo exemplo ajudará você a configurar o Node.js. Você pode substituí-lo pelo seu próprio aplicativo — certifique-se apenas de que você modifique seu aplicativo para escutar os endereços IP e portas apropriadas.

      Primeiramente, vamos criar um aplicativo exemplo chamado hello.js:

      Insira o seguinte código no arquivo:

      ~/hello.js

      const http = require('http');
      
      const hostname="localhost";
      const port = 3000;
      
      const server = http.createServer((req, res) => {
        res.statusCode = 200;
        res.setHeader('Content-Type', 'text/plain');
        res.end('Hello World!n');
      });
      
      server.listen(port, hostname, () => {
        console.log(`Server running at http://${hostname}:${port}/`);
      });
      

      Salve o arquivo e saia do editor.

      Este aplicativo Node.js escuta no endereço especificado (localhost) e porta (3000) e retorna “Hello World!” com um código de sucesso HTTP 200. Uma vez que estamos escutando no localhost, clientes remotos não poderão se conectar ao nosso aplicativo.

      Para testar seu aplicativo, digite:

      Você receberá o seguinte resultado:

      Output

      Server running at http://localhost:3000/

      Nota: executar um aplicativo Node.js dessa maneira irá bloquear comandos adicionais até que o aplicativo seja encerrado pressionando-se CTRL+C.

      Para testar o aplicativo, abra outra sessão de terminal no seu servidor e conecte-se ao localhost com o curl:

      • curl http://localhost:3000

      Caso obtenha o seguinte resultado, o aplicativo está funcionando corretamente e escutando no endereço e porta corretos:

      Output

      Hello World!

      Caso não obtenha o resultado esperado, certifique-se de que seu aplicativo Node.js esteja funcionando e configurado para escutar no endereço e porta apropriados.

      Assim que tiver certeza certeza de que ele está funcionando, encerre o aplicativo (se você ainda não o tiver feito) pressionando CTRL+C.

      Passo 3 — Instalando o PM2

      Em seguida, vamos instalar o PM2, um gerenciador de processos para aplicativos Node.js. O PM2 torna possível forçar os aplicativos a executarem como daemon para que eles executem em segundo plano como um serviço.

      Utilize o npm para instalar a última versão do PM2 no seu servidor:

      • sudo npm install pm2@latest -g

      A opção -g faz com que o npm instale o módulo globally, para que ele esteja disponível em todo o sistema.

      Vamos usar primeiro o comando pm2 start para executar seu aplicativo, hello.js, em segundo plano:

      Isso também adiciona seu aplicativo na lista de processos do PM2, que é produzida toda vez que você inicia um aplicativo:

      Output

      ... [PM2] Spawning PM2 daemon with pm2_home=/home/sammy/.pm2 [PM2] PM2 Successfully daemonized [PM2] Starting /home/sammy/hello.js in fork_mode (1 instance) [PM2] Done. ┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐ │ id │ name │ mode │ ↺ │ status │ cpu │ memory │ ├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤ │ 0 │ hello │ fork │ 0 │ online │ 0% │ 25.2mb │ └────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘

      Assim como indicado acima, o PM2 atribui automaticamente um App name (baseado no nome do arquivo, sem a extensão .js) e um id do PM2. O PM2 também mantém outras informações, como o PID do processo, seu status atual e o uso de memória.

      Os aplicativos que estão funcionando sob o PM2 serão reiniciados automaticamente se o aplicativo falhar ou for encerrado, mas podemos ir um passo além para fazer o aplicativo iniciar na inicialização do sistema usando o subcomando startup. Este subcomando gera e configura um script de inicialização para iniciar o PM2 e seus processos gerenciados nas inicializações do servidor:

      A última linha da saída resultante incluirá um comando para ser executado com privilégios de superusuário para definir o PM2 para iniciar na inicialização:

      Output

      [PM2] Init System found: systemd sammy [PM2] To setup the Startup Script, copy/paste the following command: sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy

      Execute o comando a partir do resultado, com o seu nome de usuário no lugar de sammy:

      • sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy

      Como um passo adicional, podemos salvar a lista de processos PM2 e os ambientes correspondentes:

      Agora, você criou uma unidade systemd que executa o pm2 para seu usuário na inicialização. Esta instância pm2, por sua vez, executa o hello.js.

      Inicie o serviço com systemctl:

      • sudo systemctl start pm2-sammy

      Se neste ponto você encontrar um erro, pode ser necessário reinicializar o sistema. Isso pode ser feito com o sudo reboot.

      Verifique o status da unidade systemd:

      • systemctl status pm2-sammy

      Para um panorama detalhado do systemd, veja Systemd Essentials: Working with Services, Units, and the Journal.

      Além daqueles que abordamos, o PM2 fornece muitos subcomandos que permitem que você gerencie ou procure informações sobre seus aplicativos.

      Interrompa um aplicativo com este comando (especifique o App name do PM2 ou id):

      Reinicie um aplicativo:

      • pm2 restart app_name_or_id

      Liste os aplicativos atualmente gerenciados pelo PM2:

      Obtenha informações sobre um aplicativo específico usando seu App name:

      O monitor de processos do PM2 pode ser trazido com o subcomando monit. Isso mostra o status do aplicativo, da CPU, e o uso de memória:

      Note que executar o pm2 sem qualquer argumento também exibirá uma página de ajuda com exemplos de uso.

      Agora que seu aplicativo Node.js está funcionando e é gerenciado pelo PM2, vamos configurar o proxy reverso.

      Passo 4 — Configurando o Nginx como um Servidor de Proxy Reverso

      Seu aplicativo está funcionando e escutando no localhost, mas você precisa configurar uma forma dos seus usuários acesssarem ele. Vamos configurar o servidor Web Nginx como um proxy reverso com esse intuito.

      Nos pré-requisitos do tutorial, você definiu sua configuração do Nginx no arquivo /etc/nginx/sites-available/example.com. Abra este arquivo para edição:

      • sudo nano /etc/nginx/sites-available/example.com

      Dentro do bloco server, você deve ter um bloco location / existente. Substitua o conteúdo desse bloco com a seguinte configuração. Se seu aplicativo for configurado para escutar em uma porta diferente, atualize a parte em destaque com o número de porta correto:

      /etc/nginx/sites-available/example.com

      server {
      ...
          location / {
              proxy_pass http://localhost:3000;
              proxy_http_version 1.1;
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection 'upgrade';
              proxy_set_header Host $host;
              proxy_cache_bypass $http_upgrade;
          }
      ...
      }
      

      Isso configura o servidor para responder a pedidos em sua raiz. Supondo que nosso servidor esteja disponível em example.com, acessar o https://example.com/ através de um navegador Web enviaria o pedido para hello.js, escutando na porta 3000 do localhost.

      Você pode adicionar blocos location adicionais ao mesmo bloco de servidor para fornecer acesso a outros aplicativos no mesmo servidor. Por exemplo, se você também estivesse executando outro aplicativo Node.js na porta 3001, você poderia adicionar este bloco de localização para permitir o acesso a ele através de https://example.com/app2:

      /etc/nginx/sites-available/example.com — Optional

      server {
      ...
          location /app2 {
              proxy_pass http://localhost:3001;
              proxy_http_version 1.1;
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection 'upgrade';
              proxy_set_header Host $host;
              proxy_cache_bypass $http_upgrade;
          }
      ...
      }
      

      Assim que terminar de adicionar os blocos de localização para seus aplicativos, salve o arquivo e saia do seu editor.

      Certifique-se de que não tenha introduzido qualquer erro de sintaxe digitando:

      Reinicie o Nginx:

      • sudo systemctl restart nginx

      Supondo que o seu aplicativo Node.js esteja funcionando e o seu aplicativo e as configurações do Nginx estejam corretos, agora você deverá poder acessar seu aplicativo via proxy reverso do Nginx. Teste acessando o URL do seu servidor (seu endereço IP público ou nome de domínio).

      Conclusão

      Parabéns! Agora, você tem seu aplicativo Node.js funcionando atrás de um proxy reverso Nginx em um servidor Ubuntu 20.04. Esta configuração de proxy reverso é suficientemente flexível para fornecer o acesso aos seus usuários a outros aplicativos ou conteúdos Web estáticos que você queira compartilhar.



      Source link

      Настройка приложения Node.js для работы в среде Ubuntu 20.04


      Введение

      Node.js — среда исполнения JavaScript с открытым исходным кодом, предназначенная для построения серверных и сетевых приложений. Данная платформа работает в операционных системах Linux, macOS, FreeBSD и Windows. Хотя вы можете запускать приложения Node.js через командную строку, этот обучающий модуль посвящен их запуску в качестве службы. Это означает, что они будут перезапускаться при перезагрузке системы или неисправности, и что их можно безопасно использовать в производственной среде.

      В этом обучающем модуле вы научитесь создавать готовую производственную среду Node.js на одном сервере Ubuntu 20.04. Этот сервер будет выполнять приложение Node.js под управлением PM2 и предоставлять пользователям безопасный доступ к приложению через обратный прокси-сервер Nginx. Сервер Nginx обеспечивает поддержку HTTPS с использованием бесплатного сертификата от Let’s Encrypt.

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

      Этот обучающий модуль предполагает, что у вас имеется следующее:

      Если предварительные требования выполнены, у вас должен быть сервер, обслуживающий используемую по умолчанию страницу назначения вашего домена по адресу https://example.com/.

      Шаг 1 — Установка Node.js

      Для начала мы установим самый быстрый выпуск LTS Node.js, используя архивы пакетов NodeSource.

      Вначале мы установим NodeSource PPA, чтобы получить доступ к его содержимому. Убедитесь, что вы находитесь в домашнем каталоге, и используйте curl для получения скрипта установки для последней версии LTS Node.js из его архивов.

      • cd ~
      • curl -sL https://deb.nodesource.com/setup_14.x -o nodesource_setup.sh

      Вы можете просмотреть содержимое скрипта с помощью nano или предпочитаемого текстового редактора:

      Завершив проверку скрипта, запустите его от имени пользователя sudo:

      • sudo bash nodesource_setup.sh

      Архив PPA будет добавлен в вашу конфигурацию и кэш локальных пакетов автоматически обновится. После запуска скрипта установки Nodesource вы можете установить пакет Node.js:

      Чтобы проверить номер версии Node.js, установленной на начальном шаге, введите:

      Output

      v14.4.0

      Примечание. При установке из NodeSource PPA исполняемый файл Node.js имеет имя nodejs, а не node.

      Пакет nodejs содержит двоичный файл nodejs, а также диспетчер пакетов npm для модулей Node, так что отдельно устанавливать npm не нужно.

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

      Output

      6.14.5

      Для работы некоторых пакетов npm (например, требующих компиляцию кода из источника) потребуется установить пакет build-essential:

      • sudo apt install build-essential

      Теперь у вас есть необходимые инструменты для работы с пакетами npm, которые требуют компиляции кода из источника.

      Установив исполняемый модуль Node.js, мы можем перейти к написанию приложения Node.js.

      Шаг 2 — Создание приложения Node.js

      Напишем приложение Hello World, возвращающее «Hello World» в ответ на любые запросы HTTP. Этот образец приложения поможет вам выполнить настройку Node.js. Вы можете заменить его собственным приложением, но при этом обязательно измените приложение для прослушивания подходящих IP-адресов и портов.

      Вначале создадим образец приложения под именем hello.js:

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

      ~/hello.js

      const http = require('http');
      
      const hostname="localhost";
      const port = 3000;
      
      const server = http.createServer((req, res) => {
        res.statusCode = 200;
        res.setHeader('Content-Type', 'text/plain');
        res.end('Hello World!n');
      });
      
      server.listen(port, hostname, () => {
        console.log(`Server running at http://${hostname}:${port}/`);
      });
      

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

      Это приложение Node.js прослушивает заданный адрес (localhost) и порт (3000) и возвращает текст «Hello World!» с кодом успешного выполнения a 200 HTTP. Поскольку мы прослушиваем localhost, удаленные клиенты не смогут подключиться к нашему приложению.

      Чтобы протестировать приложение, введите:

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

      Output

      Server running at http://localhost:3000/

      Примечание. Такой способ запуска приложения Node.js блокирует дополнительные команды, пока приложение не будет закрыто нажатием CTRL+C.

      Чтобы протестировать приложение, откройте на сервере другой сеанс терминала и подключитесь к localhost с помощью команды curl:

      • curl http://localhost:3000

      Если вы увидите следующий результат, приложение работает нормально и прослушивает правильные адрес и порт:

      Output

      Hello World!

      Если вы не увидите ожидаемого результата, убедитесь, что ваше приложение Node.js запущено и настроено для прослушивание правильных адреса и порта.

      Убедившись, что приложение работает, остановите его (если еще не сделали этого) нажатием CTRL+C.

      Шаг 3 — Установка PM2

      Теперь установим диспетчер процессов PM2, предназначенный для приложений Node.js. PM2 позволяет преобразовывать приложения в демонов, чтобы они работали как службы в фоновом режиме.

      Используйте npm для установки последней версии PM2 на своем сервере:

      • sudo npm install pm2@latest -g

      Опция -g указывает npm выполнить глобальную установку модуля, чтобы он был доступен в масштабе всей системы.

      Вначале используем команду pm2 для запуска вашего приложения hello.js в фоновом режиме:

      Также она добавит ваше приложение в список процессов PM2, которы йвыводится при каждом запуске приложения:

      Output

      ... [PM2] Spawning PM2 daemon with pm2_home=/home/sammy/.pm2 [PM2] PM2 Successfully daemonized [PM2] Starting /home/sammy/hello.js in fork_mode (1 instance) [PM2] Done. ┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐ │ id │ name │ mode │ ↺ │ status │ cpu │ memory │ ├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤ │ 0 │ hello │ fork │ 0 │ online │ 0% │ 25.2mb │ └────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘

      Как указано выше, PM2 автоматически присваивает App name (основанное на имени файла, без расширения .js) и PM2 id. PM2 также обслуживает другие данные, такие как PID процесса, его текущее состояние и использование памяти.

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

      Последняя строка результатов содержит команду, которую нужно запустить с привилегиями суперпользователя для настройки запуска PM2 при загрузке:

      Output

      [PM2] Init System found: systemd sammy [PM2] To setup the Startup Script, copy/paste the following command: sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy

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

      • sudo env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemd -u sammy --hp /home/sammy

      Дополнительно мы можем сохранить список процессов PM2 и соответствующие среды:

      Теперь вы создали блок systemd, который запускает pm2 для вашего пользователя при загрузке. Этот экземпляр pm2 запускает hello.js.

      Запустите службу с помощью команды systemctl:

      • sudo systemctl start pm2-sammy

      Если на этом этапе вы увидите ошибку, вам может потребоваться перезагрузка, которую можно выполнить с помощью sudo reboot.

      Проверьте состояние блока systemd:

      • systemctl status pm2-sammy

      Подробный обзор блока systemd можно найти в разделе Основы работы с Systemd: работа со службами, блоками и журналом.

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

      Остановите приложение с помощью этой команды (укажите имя приложения PM2 или id):

      Перезапустите приложение:

      • pm2 restart app_name_or_id

      Выведем список приложений, управление которыми осуществляет PM2:

      Получим информацию об определенном приложении по имени приложения:

      Монитор процесса PM2 запускается с помощью субкоманды monit. При этом отображается состояние приложение, использование ресурсов ЦП и использование памяти:

      При запуске команды pm2 без аргументов отображается страница справки с примерами использования.

      Теперь ваше приложение Node.js запущено и управляется PM2, и мы можем настроить обратный прокси-сервер.

      Шаг 4 — Настройка Nginx в качестве обратного прокси-сервера

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

      В предварительных обучающих модулях вы настроили конфигурацию Nginx в файле /etc/nginx/sites-available/example.com. Откройте этот файл для редактирования:

      • sudo nano /etc/nginx/sites-available/example.com

      В блоке server должен содержаться блок location /. Замените содержимое этого блока следующей конфигурацией. Если ваше приложение настроено для прослушивания другого порта, измените номер порта в выделенной части на подходящий:

      /etc/nginx/sites-available/example.com

      server {
      ...
          location / {
              proxy_pass http://localhost:3000;
              proxy_http_version 1.1;
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection 'upgrade';
              proxy_set_header Host $host;
              proxy_cache_bypass $http_upgrade;
          }
      ...
      }
      

      Так сервер настраивается для ответа на запросы root. Если наш сервер доступен по адресу example.com, при попытке доступа к https://example.com/ через браузер будет отправлен запрос hello.js с прослушиванием порта 3000 хоста localhost.

      Вы можете добавить в этот же серверный блок дополнительные блоки location, чтобы предоставить другим приложениям доступ к этому же серверу. Например, если вы используете другое приложение Node.js на порту 3001, вы сможете добавить следующий блок location, чтобы разрешить доступ к нему через https://example.com/app2:

      /etc/nginx/sites-available/example.com — Optional

      server {
      ...
          location /app2 {
              proxy_pass http://localhost:3001;
              proxy_http_version 1.1;
              proxy_set_header Upgrade $http_upgrade;
              proxy_set_header Connection 'upgrade';
              proxy_set_header Host $host;
              proxy_cache_bypass $http_upgrade;
          }
      ...
      }
      

      Завершив добавление блоков location для ваших приложений, сохраните файл и закройте редактор.

      Убедитесь в отсутствии ошибок синтаксиса с помощью следующей команды:

      Перезапустите Nginx:

      • sudo systemctl restart nginx

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

      Заключение

      Поздравляем! Теперь у вас есть приложение Node.js, работающее за обратным прокси-сервером Nginx на сервере Ubuntu 20.04. Настройка обратного прокси-сервера достаточно гибкая, чтобы предоставить вашим пользователям доступ к другим приложениям или статическому веб-контенту, который вы хотите опубликовать.



      Source link

      How To Launch Child Processes in Node.js


      The author selected the COVID-19 Relief Fund to receive a donation as part of the Write for DOnations program.

      Introduction

      When a user executes a single Node.js program, it runs as a single operating system (OS) process that represents the instance of the program running. Within that process, Node.js executes programs on a single thread. As mentioned earlier in this series with the How To Write Asynchronous Code in Node.js tutorial, because only one thread can run on one process, operations that take a long time to execute in JavaScript can block the Node.js thread and delay the execution of other code. A key strategy to work around this problem is to launch a child process, or a process created by another process, when faced with long-running tasks. When a new process is launched, the operating system can employ multiprocessing techniques to ensure that the main Node.js process and the additional child process run concurrently, or at the same time.

      Node.js includes the child_process module, which has functions to create new processes. Aside from dealing with long-running tasks, this module can also interface with the OS and run shell commands. System administrators can use Node.js to run shell commands to structure and maintain their operations as a Node.js module instead of shell scripts.

      In this tutorial, you will create child processes while executing a series of sample Node.js applications. You’ll create processes with the child_process module by retrieving the results of a child process via a buffer or string with the exec() function, and then from a data stream with the spawn() function. You’ll finish by using fork() to create a child process of another Node.js program that you can communicate with as it’s running. To illustrate these concepts, you will write a program to list the contents of a directory, a program to find files, and a web server with multiple endpoints.

      Prerequisites

      Step 1 — Creating a Child Process with exec()

      Developers commonly create child processes to execute commands on their operating system when they need to manipulate the output of their Node.js programs with a shell, such as using shell piping or redirection. The exec() function in Node.js creates a new shell process and executes a command in that shell. The output of the command is kept in a buffer in memory, which you can accept via a callback function passed into exec().

      Let’s begin creating our first child processes in Node.js. First, we need to set up our coding environment to store the scripts we’ll create throughout this tutorial. In the terminal, create a folder called child-processes:

      Enter that folder in the terminal with the cd command:

      Create a new file called listFiles.js and open the file in a text editor. In this tutorial we will use nano, a terminal text editor:

      We’ll be writing a Node.js module that uses the exec() function to run the ls command. The ls command list the files and folders in a directory. This program takes the output from the ls command and displays it to the user.

      In the text editor, add the following code:

      ~/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}`);
      });
      

      We first import the exec() command from the child_process module using JavaScript destructuring. Once imported, we use the exec() function. The first argument is the command we would like to run. In this case, it’s ls -lh, which lists all the files and folders in the current directory in long format, with a total file size in human-readable units at the top of the output.

      The second argument is a callback function with three parameters: error, stdout, and stderr. If the command failed to run, error will capture the reason why it failed. This can happen if the shell cannot find the command you’re trying to execute. If the command is executed successfully, any data it writes to the standard output stream is captured in stdout, and any data it writes to the standard error stream is captured in stderr.

      Note: It’s important to keep the difference between error and stderr in mind. If the command itself fails to run, error will capture the error. If the command runs but returns output to the error stream, stderr will capture it. The most resilient Node.js programs will handle all possible outputs for a child process.

      In our callback function, we first check if we received an error. If we did, we display the error’s message (a property of the Error object) with console.error() and end the function with return. We then check if the command printed an error message and return if so. If the command successfully executes, we log its output to the console with console.log().

      Let’s run this file to see it in action. First, save and exit nano by pressing CTRL+X.

      Back in your terminal, run your application with the node command:

      Your terminal will display the following output:

      Output

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

      This lists the contents of the child-processes directory in long format, along with the size of the contents at the top. Your results will have your own user and group in place of sammy. This shows that the listFiles.js program successfully ran the shell command ls -lh.

      Now let’s look at another way to execute concurrent processes. Node.js’s child_process module can also run executable files with the execFile() function. The key difference between the execFile() and exec() functions is that the first argument of execFile() is now a path to an executable file instead of a command. The output of the executable file is stored in a buffer like exec(), which we access via a callback function with error, stdout, and stderr parameters.

      Note: Scripts in Windows such as .bat and .cmd files cannot be run with execFile() because the function does not create a shell when running the file. On Unix, Linux, and macOS, executable scripts do not always need a shell to run. However, a Windows machines needs a shell to execute scripts. To execute script files on Windows, use exec(), since it creates a new shell. Alternatively, you can use spawn(), which you’ll use later in this Step.

      However, note that you can execute .exe files in Windows successfully using execFile(). This limitation only applies to script files that require a shell to execute.

      Let’s begin by adding an executable script for execFile() to run. We’ll write a bash script that downloads the Node.js logo from the Node.js website and Base64 encodes it to convert its data to a string of ASCII characters.

      Create a new shell script file called processNodejsImage.sh:

      • nano processNodejsImage.sh

      Now write a script to download the image and base64 convert it:

      ~/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
      

      The first statement is a shebang statement. It’s used in Unix, Linux, and macOS when we want to specify a shell to execute our script. The second statement is a curl command. The cURL utility, whose command is curl, is a command-line tool that can transfer data to and from a server. We use cURL to download the Node.js logo from the website, and then we use redirection to save the downloaded data to a new file nodejs-logo.svg. The last statement uses the base64 utility to encode the nodejs-logo.svg file we downloaded with cURL. The script then outputs the encoded string to the console.

      Save and exit before continuing.

      In order for our Node program to run the bash script, we have to make it executable. To do this, run the following:

      • chmod u+x processNodejsImage.sh

      This will give your current user the permission to execute the file.

      With our script in place, we can write a new Node.js module to execute it. This script will use execFile() to run the script in a child process, catching any errors and displaying all output to console.

      In your terminal, make a new JavaScript file called getNodejsImage.js:

      Type the following code in the text editor:

      ~/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}`);
      });
      

      We use JavaScript destructuring to import the execFile() function from the child_process module. We then use that function, passing the file path as the first name. __dirname contains the directory path of the module in which it is written. Node.js provides the __dirname variable to a module when the module runs. By using __dirname, our script will always find the processNodejsImage.sh file across different operating systems, no matter where we run getNodejsImage.js. Note that for our current project setup, getNodejsImage.js and processNodejsImage.sh must be in the same folder.

      The second argument is a callback with the error, stdout, and stderr parameters. Like with our previous example that used exec(), we check for each possible output of the script file and log them to the console.

      In your text editor, save this file and exit from the editor.

      In your terminal, use node to execute the module:

      Running this script will produce output like this:

      Output

      stdout: PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB2aWV3Qm94PSIwIDAgNDQyLjQgMjcwLjkiPjxkZWZzPjxsaW5lYXJHcmFkaWVudCBpZD0iYiIgeDE9IjE4MC43IiB5MT0iODAuNyIge ...

      Note that we truncated the output in this article because of its large size.

      Before base64 encoding the image, processNodejsImage.sh first downloads it. You can also verify that you downloaded the image by inspecting the current directory.

      Execute listFiles.js to find the updated list of files in our directory:

      The script will display content similar to the following on the terminal:

      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

      We’ve now successfully executed processNodejsImage.sh as a child process in Node.js using the execFile() function.

      The exec() and execFile() functions can run commands on the operating system’s shell in a Node.js child process. Node.js also provides another method with similar functionality, spawn(). The difference is that instead of getting the output of the shell commands all at once, we get them in chunks via a stream. In the next section we’ll use the spawn() command to create a child process.

      Step 2 — Creating a Child Process with spawn()

      The spawn() function runs a command in a process. This function returns data via the stream API. Therefore, to get the output of the child process, we need to listen for stream events.

      Streams in Node.js are instances of event emitters. If you would like to learn more about listening for events and the foundations of interacting with streams, you can read our guide on Using Event Emitters in Node.js.

      It’s often a good idea to choose spawn() over exec() or execFile() when the command you want to run can output a large amount of data. With a buffer, as used by exec() and execFile(), all the processed data is stored in the computer’s memory. For large amounts of data, this can degrade system performance. With a stream, the data is processed and transferred in small chunks. Therefore, you can process a large amount of data without using too much memory at any one time.

      Let’s see how we can use spawn() to make a child process. We will write a new Node.js module that creates a child process to run the find command. We will use the find command to list all the files in the current directory.

      Create a new file called findFiles.js:

      In your text editor, begin by calling the spawn() command:

      ~/child-processes/findFiles.js

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

      We first imported the spawn() function from the child_process module. We then called the spawn() function to create a child process that executes the find command. We hold the reference to the process in the child variable, which we will use to listen to its streamed events.

      The first argument in spawn() is the command to run, in this case find. The second argument is an array that contains the arguments for the executed command. In this case, we are telling Node.js to execute the find command with the argument ., thereby making the command find all the files in the current directory. The equivalent command in the terminal is find ..

      With the exec() and execFile() functions, we wrote the arguments along with the command in one string. However, with spawn(), all arguments to commands must be entered in the array. That’s because spawn(), unlike exec() and execFile(), does not create a new shell before running a process. To have commands with their arguments in one string, you need Node.js to create a new shell as well.

      Let’s continue our module by adding listeners for the command’s output. Add the following highlighted lines:

      ~/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}`);
      });
      

      Commands can return data in either the stdout stream or the stderr stream, so you added listeners for both. You can add listeners by calling the on() method of each streams’ objects. The data event from the streams gives us the command’s output to that stream. Whenever we get data on either stream, we log it to the console.

      We then listen to two other events: the error event if the command fails to execute or is interrupted, and the close event for when the command has finished execution, thus closing the stream.

      In the text editor, complete the Node.js module by writing the following highlighted lines:

      ~/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}`);
      });
      

      For the error and close events, you set up a listener directly on the child variable. When listening for error events, if one occurs Node.js provides an Error object. In this case, you log the error’s message property.

      When listening to the close event, Node.js provides the exit code of the command. An exit code denotes if the command ran successfully or not. When a command runs without errors, it returns the lowest possible value for an exit code: 0. When executed with an error, it returns a non-zero code.

      The module is complete. Save and exit nano with CTRL+X.

      Now, run the code with the node command:

      Once complete, you will find the following output:

      Output

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

      We find a list of all files in our current directory and the exit code of the command, which is 0 as it ran successfully. While our current directory has a small number of files, if we ran this code in our home directory, our program would list every single file in every accessible folder for our user. Because it has such a potentially large output, using the spawn() function is most ideal as its streams do not require as much memory as a large buffer.

      So far we’ve used functions to create child processes to execute external commands in our operating system. Node.js also provides a way to create a child process that executes other Node.js programs. Let’s use the fork() function to create a child process for a Node.js module in the next section.

      Step 3 — Creating a Child Process with fork()

      Node.js provides the fork() function, a variation of spawn(), to create a child process that’s also a Node.js process. The main benefit of using fork() to create a Node.js process over spawn() or exec() is that fork() enables communication between the parent and the child process.

      With fork(), in addition to retrieving data from the child process, a parent process can send messages to the running child process. Likewise, the child process can send messages to the parent process.

      Let’s see an example where using fork() to create a new Node.js child process can improve the performance of our application. Node.js programs run on a single process. Therefore, CPU intensive tasks like iterating over large loops or parsing large JSON files stop other JavaScript code from running. For certain applications, this is not a viable option. If a web server is blocked, then it cannot process any new incoming requests until the blocking code has completed its execution.

      Let’s see this in practice by creating a web server with two endpoints. One endpoint will do a slow computation that blocks the Node.js process. The other endpoint will return a JSON object saying hello.

      First, create a new file called httpServer.js, which will have the code for our HTTP server:

      We’ll begin by setting up the HTTP server. This involves importing the http module, creating a request listener function, creating a server object, and listening for requests on the server object. If you would like to dive deeper into creating HTTP servers in Node.js or would like a refresher, you can read our guide on How To Create a Web Server in Node.js with the HTTP Module.

      Enter the following code in your text editor to set up an HTTP server:

      ~/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}`);
      });
      

      This code sets up an HTTP server that will run at http://localhost:8000. It uses template literals to dynamically generate that URL.

      Next, we will write an intentionally slow function that counts in a loop 5 billion times. Before the requestListener() function, add the following code:

      ~/child-processes/httpServer.js

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

      This uses the arrow function syntax to create a while loop that counts to 5000000000.

      To complete this module, we need to add code to the requestListener() function. Our function will call the slowFunction() on subpath, and return a small JSON message for the other. Add the following code to the module:

      ~/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"}`);
        }
      };
      ...
      

      If the user reaches the server at the /total subpath, then we run slowFunction(). If we are hit at the /hello subpath, we return this JSON message: {"message":"hello"}.

      Save and exit the file by pressing CTRL+X.

      To test, run this server module with node:

      When our server starts, the console will display the following:

      Output

      Server is running on http://localhost:8000

      Now, to test the performance of our module, open two additional terminals. In the first terminal, use the curl command to make a request to the /total endpoint, which we expect to be slow:

      • curl http://localhost:8000/total

      In the other terminal, use curl to make a request to the /hello endpoint like this:

      • curl http://localhost:8000/hello

      The first request will return the following JSON:

      Output

      {"totalCount":5000000000}

      Whereas the second request will return this JSON:

      Output

      {"message":"hello"}

      The request to /hello completed only after the request to /total. The slowFunction() blocked all other code from executing while it was still in its loop. You can verify this by looking at the Node.js server output that was logged in your original terminal:

      Output

      Returning /total results Returning /hello results

      To process the blocking code while still accepting incoming requests, we can move the blocking code to a child process with fork(). We will move the blocking code into its own module. The Node.js server will then create a child process when someone accesses the /total endpoint and listen for results from this child process.

      Refactor the server by first creating a new module called getCount.js that will contain slowFunction():

      Now enter the code for slowFunction() once again:

      ~/child-processes/getCount.js

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

      Since this module will be a child process created with fork(), we can also add code to communicate with the parent process when slowFunction() has completed processing. Add the following block of code that sends a message to the parent process with the JSON to return to the user:

      ~/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);
        }
      });
      

      Let’s break down this block of code. The messages between a parent and child process created by fork() are accessible via the Node.js global process object. We add a listener to the process variable to look for message events. Once we receive a message event, we check if it’s the START event. Our server code will send the START event when someone accesses the /total endpoint. Upon receiving that event, we run slowFunction() and create a JSON string with the result of the function. We use process.send() to send a message to the parent process.

      Save and exit getCount.js by entering CTRL+X in nano.

      Now, let’s modify the httpServer.js file so that instead of calling slowFunction(), it creates a child process that executes getCount.js.

      Re-open httpServer.js with nano:

      First, import the fork() function from the child_process module:

      ~/child-processes/httpServer.js

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

      Next, we are going to remove the slowFunction() from this module and modify the requestListener() function to create a child process. Change the code in your file so it looks like this:

      ~/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"}`);
        }
      };
      ...
      

      When someone goes to the /total endpoint, we now create a new child process with fork(). The argument of fork() is the path to the Node.js module. In this case, it is the getCount.js file in our current directory, which we receive from __dirname. The reference to this child process is stored in a variable child.

      We then add a listener to the child object. This listener captures any messages that the child process gives us. In this case, getCount.js will return a JSON string with the total number counted by the while loop. When we receive that message, we send the JSON to the user.

      We use the send() function of the child variable to give it a message. This program sends the message START, which begins the execution of slowFunction() in the child process.

      Save and exit nano by entering CTRL+X.

      To test the improvement using fork() made on HTTP server, begin by executing the httpServer.js file with node:

      Like before, it will output the following message when it launches:

      Output

      Server is running on http://localhost:8000

      To test the server, we will need an additional two terminals as we did the first time. You can re-use them if they are still open.

      In the first terminal, use the curl command to make a request to the /total endpoint, which takes a while to compute:

      • curl http://localhost:8000/total

      In the other terminal, use curl to make a request to the /hello endpoint, which responds in a short time:

      • curl http://localhost:8000/hello

      The first request will return the following JSON:

      Output

      {"totalCount":5000000000}

      Whereas the second request will return this JSON:

      Output

      {"message":"hello"}

      Unlike the first time we tried this, the second request to /hello runs immediately. You can confirm by reviewing the logs, which will look like this:

      Output

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

      These logs show that the request for the /hello endpoint ran after the child process was created but before the child process had finished its task.

      Since we moved the blocking code in a child process using fork(), the server was still able to respond to other requests and execute other JavaScript code. Because of the fork() function’s message passing ability, we can control when a child process begins an activity and we can return data from a child process to a parent process.

      Conclusion

      In this article, you used various functions to create a child process in Node.js. You first created child processes with exec() to run shell commands from Node.js code. You then ran an executable file with the execFile() function. You looked at the spawn() function, which can also run commands but returns data via a stream and does not start a shell like exec() and execFile(). Finally, you used the fork() function to allow for two-way communication between the parent and child process.

      To learn more about the child_process module, you can read the Node.js documentation. If you’d like to continue learning Node.js, you can return to the How To Code in Node.js series, or browse programming projects and setups on our Node topic page.



      Source link