Модуль process — управление процессом
Зачем нужно
process -- глобальный объект Node.js, дающий доступ к текущему процессу: переменные окружения, аргументы командной строки, стандартные потоки ввода/вывода, управление завершением и обработка системных сигналов. Это интерфейс между вашим кодом и операционной системой.
Где используется
- Чтение переменных окружения (
DATABASE_URL,NODE_ENV,PORT) - Парсинг аргументов CLI-инструментов
- Graceful shutdown серверов (SIGTERM, SIGINT)
- Логирование в stdout/stderr
- Глобальная обработка необработанных ошибок
- Мониторинг использования памяти
Предпосылки
- Что такое Node.js — однопоточная модель
- Event Loop в Node — process.nextTick
- Базовое понимание процессов ОС и переменных окружения
Импорт
// process — глобальный, импорт не обязателен
console.log(process.version);
// Но можно импортировать явно (ESM)
import process from 'process';
// Или в CommonJS
const process = require('process');
process.env — переменные окружения
// Чтение переменных окружения
console.log(process.env.NODE_ENV); // 'production', 'development'
console.log(process.env.PORT); // '3000' (всегда строка!)
console.log(process.env.DATABASE_URL); // 'postgres://...'
console.log(process.env.HOME); // '/home/user'
console.log(process.env.PATH); // '/usr/local/bin:...'
// Задание переменных при запуске
// PORT=8080 NODE_ENV=production node app.js
// ⚠️ process.env значения — ВСЕГДА строки
const port = process.env.PORT;
console.log(typeof port); // 'string'
const portNum = parseInt(process.env.PORT, 10) || 3000;
// Можно устанавливать в рантайме (только для текущего процесса)
process.env.MY_VAR = 'value';
Паттерн конфигурации
// config.js
const config = {
port: parseInt(process.env.PORT, 10) || 3000,
host: process.env.HOST || 'localhost',
nodeEnv: process.env.NODE_ENV || 'development',
db: {
url: process.env.DATABASE_URL || 'postgres://localhost:5432/mydb',
},
isDev: process.env.NODE_ENV !== 'production',
isProd: process.env.NODE_ENV === 'production',
};
module.exports = config;
// app.js
const config = require('./config');
server.listen(config.port, config.host);
.env файлы с dotenv
# .env (не коммитить в git!)
PORT=3000
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
JWT_SECRET=super-secret-key
NODE_ENV=development
// Установка: npm install dotenv
require('dotenv').config; // загружает .env в process.env
// Или в Node.js 20.6+:
// node --env-file=.env app.js
process.argv — аргументы командной строки
// node app.js --port 8080 --verbose
console.log(process.argv);
// [
// '/usr/local/bin/node', // [0] — путь к node
// '/home/user/app.js', // [1] — путь к скрипту
// '--port', // [2] — первый аргумент
// '8080', // [3]
// '--verbose' // [4]
// ]
// Аргументы пользователя начинаются с [2]
const args = process.argv.slice(2);
console.log(args); // ['--port', '8080', '--verbose']
Простой парсинг аргументов
// node app.js --port 8080 --host localhost --verbose
function parseArgs(args) {
const result = {};
for (let i = 0; i < args.length; i++) {
if (args[i].startsWith('--')) {
const key = args[i].slice(2);
const next = args[i + 1];
if (next && !next.startsWith('--')) {
result[key] = next;
i++; // пропустить значение
} else {
result[key] = true; // флаг без значения
}
}
}
return result;
}
const options = parseArgs(process.argv.slice(2));
// { port: '8080', host: 'localhost', verbose: true }
parseArgs (Node.js 18.3+)
const { parseArgs } = require('util');
// node app.js --port 8080 --verbose --name myapp
const { values } = parseArgs({
options: {
port: { type: 'string', short: 'p', default: '3000' },
verbose: { type: 'boolean', short: 'v', default: false },
name: { type: 'string', short: 'n' },
},
strict: true,
args: process.argv.slice(2),
});
console.log(values);
// { port: '8080', verbose: true, name: 'myapp' }
Стандартные потоки: stdin, stdout, stderr
// stdout — обычный вывод (Writable Stream)
process.stdout.write('Hello ');
process.stdout.write('World\n');
// Эквивалент: console.log('Hello World')
// stderr — вывод ошибок (Writable Stream)
process.stderr.write('ERROR: something went wrong\n');
// Эквивалент: console.error('ERROR: something went wrong')
// Разница: stdout и stderr — разные потоки
// stdout можно перенаправить в файл, а stderr оставить в терминале:
// node app.js > output.log 2> errors.log
// node app.js > output.log (stderr по-прежнему в терминале)
stdin — чтение ввода
// Чтение из stdin (Readable Stream)
process.stdin.setEncoding('utf-8');
process.stdin.on('data', (input) => {
const trimmed = input.trim();
if (trimmed === 'exit') {
process.exit(0);
}
console.log(`Вы ввели: ${trimmed}`);
});
console.log('Введите текст (exit для выхода):');
Pipe через stdin/stdout
# Использование в pipe-цепочке
echo "hello world" | node uppercase.js
cat data.txt | node process.js > result.txt
// uppercase.js — читает stdin, пишет в stdout верхний регистр
process.stdin.setEncoding('utf-8');
let data = '';
process.stdin.on('data', (chunk) => data += chunk);
process.stdin.on('end', () => {
process.stdout.write(data.toUpperCase());
});
process.exit — завершение процесса
// Коды выхода
process.exit(0); // Успех (по умолчанию)
process.exit(1); // Общая ошибка
// Стандартные коды:
// 0 — успех
// 1 — общая ошибка
// 2 — некорректное использование (неправильные аргументы)
// 126 — команда не может быть выполнена
// 127 — команда не найдена
// 130 — прерван по Ctrl+C (128 + SIGINT=2)
// ❌ Не рекомендуется вызывать process.exit напрямую
// Это обрывает незавершённые операции (запись в файл, ответ клиенту)
// ✅ Лучше: установить exitCode и дать процессу завершиться естественно
process.exitCode = 1; // Процесс завершится с кодом 1 когда Event Loop опустеет
// Чтение кода после завершения:
// echo $? (Linux/macOS)
// echo %ERRORLEVEL% (Windows)
process.cwd и другие свойства
// Текущая рабочая директория
console.log(process.cwd);
// '/home/user/project'
// (откуда запущен node, не где лежит файл)
// Изменить рабочую директорию
process.chdir('/tmp');
// Информация о процессе
console.log(process.pid); // 12345 (ID процесса)
console.log(process.ppid); // 12340 (ID родительского процесса)
console.log(process.title); // 'node'
console.log(process.version); // 'v22.12.0'
console.log(process.versions.v8); // '12.4.254.21'
console.log(process.platform); // 'linux', 'darwin', 'win32'
console.log(process.arch); // 'x64', 'arm64'
// Использование памяти
const mem = process.memoryUsage;
console.log({
rss: `${Math.round(mem.rss / 1024 / 1024)} MB`, // Resident Set Size
heapTotal: `${Math.round(mem.heapTotal / 1024 / 1024)} MB`,
heapUsed: `${Math.round(mem.heapUsed / 1024 / 1024)} MB`,
external: `${Math.round(mem.external / 1024 / 1024)} MB`,
});
// { rss: '35 MB', heapTotal: '6 MB', heapUsed: '4 MB', external: '1 MB' }
// Время работы процесса (в секундах)
console.log(process.uptime); // 125.432
// Точное время (наносекунды) — для замеров производительности
const start = process.hrtime.bigint;
// ... тяжёлая операция ...
const end = process.hrtime.bigint;
console.log(`Заняло ${Number(end - start) / 1e6} мс`);
Системные сигналы
// SIGINT (Ctrl+C) — прерывание
process.on('SIGINT', () => {
console.log('\nПолучен SIGINT (Ctrl+C)');
cleanup;
process.exit(0);
});
// SIGTERM — запрос на завершение (Docker, systemd, PM2)
process.on('SIGTERM', () => {
console.log('Получен SIGTERM');
gracefulShutdown;
});
// Graceful shutdown паттерн
const http = require('http');
const server = http.createServer((req, res) => res.end('OK'));
function gracefulShutdown() {
console.log('Начинаем graceful shutdown...');
// 1. Перестаём принимать новые соединения
server.close(() => {
console.log('HTTP-сервер закрыт');
// 2. Закрываем соединения с БД, очереди и т.д.
// db.close();
// redis.quit;
console.log('Все ресурсы освобождены');
process.exit(0);
});
// 3. Таймаут на случай зависших соединений
setTimeout(() => {
console.error('Принудительное завершение (таймаут)');
process.exit(1);
}, 10000);
}
process.on('SIGTERM', gracefulShutdown);
process.on('SIGINT', gracefulShutdown);
server.listen(3000);
Глобальная обработка ошибок
// uncaughtException — необработанное исключение
process.on('uncaughtException', (err) => {
console.error('UNCAUGHT EXCEPTION:', err.message);
console.error(err.stack);
// ⚠️ После uncaughtException процесс в неопределённом состоянии
// НУЖНО завершить процесс после логирования
process.exit(1);
});
// unhandledRejection — необработанный Promise rejection
process.on('unhandledRejection', (reason, promise) => {
console.error('UNHANDLED REJECTION:', reason);
// В Node.js 15+ — это крашит процесс по умолчанию
// В более ранних версиях — только warning
});
// warning — предупреждения (deprecation, experimental и т.д.)
process.on('warning', (warning) => {
console.warn('WARNING:', warning.name, warning.message);
});
// ❌ Антипаттерн: ловить uncaughtException и продолжать работу
// Состояние приложения может быть повреждено
// ✅ Правильно: залогировать → завершить → перезапустить (PM2, Docker)
process.nextTick
// Выполняется ПЕРЕД следующей фазой Event Loop (см. Event Loop)
console.log('start');
process.nextTick(() => {
console.log('nextTick');
});
console.log('end');
// start
// end
// nextTick
// Применение: гарантировать асинхронный вызов callback-а
function readData(callback) {
if (cache.has(key)) {
// ❌ Синхронный вызов — нарушает контракт
// callback(null, cache.get(key));
// ✅ Асинхронный вызов — поведение всегда одинаковое
process.nextTick( => callback(null, cache.get(key)));
} else {
db.get(key, callback);
}
}
Частые ошибки
- process.env.PORT без parseInt — значение всегда строка,
'3000' + 1 === '30001' - process.exit без закрытия ресурсов — обрывает запись в файл, ответ клиенту, соединения с БД
- Игнорирование SIGTERM — Docker/K8s посылают SIGTERM перед kill, без обработчика данные теряются
- Продолжение после uncaughtException — процесс в неопределённом состоянии, нужно перезапускать
- Путают process.cwd и __dirname —
cwdзависит от того откуда запущен node,__dirname— от расположения файла - Коммитят .env в git — пароли и ключи утекают в репозиторий
Практика
- Написать скрипт, читающий
--portи--hostиз аргументов и запускающий HTTP-сервер - Реализовать graceful shutdown: SIGTERM → закрыть сервер → выйти
- Создать CLI-утилиту, читающую stdin и выводящую статистику (слова, строки, символы)
- Настроить конфигурацию через process.env + dotenv с дефолтными значениями
- Добавить мониторинг: выводить
memoryUsageкаждые 10 секунд
Связанные темы
- Event Loop в Node — process.nextTick и Event Loop
- fs — process.cwd для разрешения путей
- path — __dirname vs process.cwd
- package.json — npm scripts и переменные окружения
Ресурсы
🎓 Источник: Консоль и командная строка в JavaScript и Node.js
- 📅 2018-10-25 · YouTube · [Marp](../../../Documents/TimurShemsedinov/2018-10-25 — Консоль и командная строка в JavaScript и Node.js (5aSZyKi5BmE).md)
- Тезисы:
processдоступен глобально (без require)process.argv[0]= node,[1]= path к скрипту,[2..]= argsprocess.env— переменные окружения; читать ТОЛЬКО при старте, не на каждый запросprocess.cwd— текущий каталог откуда запущен процесс (≠__dirname)process.exit(code)— код возврата важен для CI (0 = OK, ≠ 0 = ошибка)process.stdin,process.stdout,process.stderr— стримы
🎓 Источник: Обзор встроенного Node.js API
- 📅 2018-09-26 · YouTube · [Marp](../../../Documents/TimurShemsedinov/2018-09-26 — Обзор встроенного Node.js API (sOkjR-N6IAs).md)
- Тезисы:
process.nextTick— выполнить callback СРАЗУ после текущей операции, до event loopprocess.versions— версии Node, V8, OpenSSL, libuv, ICU — отладкаprocess.memoryUsage—rss,heapTotal,heapUsed,externalprocess.exitс кодом ошибки для CI