← Назад к вопросам

Как работает в Node.js один процесс с десятью подключениями?

2.0 Middle🔥 141 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI3 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как работает в Node.js один процесс с десятью подключениями?

Event-Driven Architecture

Node.js работает на базе Event Loop — это главная концепция. Один процесс (single-threaded на уровне JavaScript, но с многопоточностью в libuv) может обрабатывать тысячи одновременных соединений благодаря неблокирующему (асинхронному) вводу-выводу.

Как работает одно соединение

// === Клиент отправляет HTTP запрос ===
// Сервер получает событие 'connection' на порту 3000

const http = require('http');

const server = http.createServer((req, res) => {
  // Это callback, который вызовется когда запрос пришёл
  console.log(`Запрос от клиента: ${req.url}`);
  
  // Обработка НЕБЛОКИРУЮЩАЯ - не ждём ничего
  res.writeHead(200, { 'Content-Type': 'text/plain' });
  res.end('OK');
});

server.listen(3000);

10 одновременных подключений: что происходит в Event Loop

// === Сценарий: 10 клиентов отправляют запросы одновременно ===

const http = require('http');
const fs = require('fs').promises;

const server = http.createServer(async (req, res) => {
  // МОМЕНТ 1: Запрос 1 приходит, начинаем обработку
  console.log(`[${Date.now()}] Начало обработки запроса`);
  
  // МОМЕНТ 2: Асинхронная операция (БД, файл, API)
  // Важно: это НЕ блокирует другие запросы!
  const data = await fs.readFile('data.json', 'utf8');
  // Пока ждём файл, Event Loop может обрабатывать запросы 2-10
  
  // МОМЕНТ 3: Данные готовы, отправляем ответ
  res.writeHead(200);
  res.end(data);
});

server.listen(3000);

// === Временная шкала (примерно) ===
// T=0ms:    Client 1: GET /api/data
// T=1ms:    Client 2: GET /api/data
// T=2ms:    Client 3: GET /api/data
// ...
// T=9ms:    Client 10: GET /api/data
// T=10ms:   Client 1: readFile() завершилась -> отправляем ответ
// T=11ms:   Client 2: readFile() завершилась -> отправляем ответ
// ...
// T=19ms:   Client 10: readFile() завершилась -> отправляем ответ

// Всё заняло ~19ms вместо 19*50ms = 950ms если бы было БЛОКИРУЮЩЕЕ

Event Loop и задачи

// === Event Loop обрабатывает задачи по очереди ===

const server = http.createServer((req, res) => {
  // Фаза 1: Синхронный код (БЫСТРО)
  const userId = req.query.id;
  
  // Фаза 2: Асинхронная операция
  database.findUser(userId, (err, user) => {
    // Callback вызовется позже, когда БД вернёт результат
    // Тем временем, Event Loop может обработать другие запросы
    res.json(user);
  });
  
  // Функция завершилась, но callback ещё выполняется в фоне
});

// === Event Loop очередь (примерно) ===
// 1. Timers (setTimeout, setInterval)
// 2. Pending callbacks (асинхронные операции)
// 3. Idle, prepare
// 4. Poll (ждёт новые события)
// 5. Check (setImmediate)
// 6. Close callbacks

// === Пример с setTimeout ===
console.log('1: Начало');

setTimeout(() => {
  console.log('2: После timeout 0ms');
}, 0);

console.log('3: Конец');

// Вывод:
// 1: Начало
// 3: Конец
// 2: После timeout 0ms
// (setTimeout 0 всё равно идёт в очередь)

Когда один процесс НЕ справляется

// === ПРОБЛЕМА: Блокирующий код ===

const server = http.createServer((req, res) => {
  // Это БЛОКИРУЮЩАЯ операция - останавливает весь Event Loop!
  const data = fs.readFileSync('big-file.json');
  
  // Пока читаем файл, ВСЕ 10 клиентов ждут
  // Event Loop заморожен
  
  res.json(JSON.parse(data));
});

// Вместо этого:
const server = http.createServer(async (req, res) => {
  // Это НЕБЛОКИРУЮЩАЯ операция - освобождает Event Loop
  const data = await fs.promises.readFile('big-file.json');
  
  // Пока читаем файл, Event Loop может обработать другие запросы
  res.json(JSON.parse(data));
});

CPU-intensive задачи: Worker Threads

// === ПРОБЛЕМА: Тяжёлые вычисления блокируют Event Loop ===

const crypto = require('crypto');

// Это будет блокировать на долгое время!
function slowHash(password) {
  return crypto.pbkdf2Sync(password, 'salt', 100000, 64, 'sha512').toString('hex');
}

const server = http.createServer((req, res) => {
  const hash = slowHash(req.body.password); // Все 10 клиентов ждут!
  res.json({ hash });
});

// === РЕШЕНИЕ: Worker Threads ===
const { Worker } = require('worker_threads');
const path = require('path');

const server = http.createServer((req, res) => {
  const worker = new Worker(path.join(__dirname, 'worker.js'));
  
  worker.on('message', (hash) => {
    res.json({ hash });
    worker.terminate();
  });
  
  worker.postMessage(req.body.password);
});

// === worker.js ===
const crypto = require('crypto');
const { parentPort } = require('worker_threads');

parentPort.on('message', (password) => {
  const hash = crypto.pbkdf2Sync(password, 'salt', 100000, 64, 'sha512').toString('hex');
  parentPort.postMessage(hash);
});

// Теперь вычисления идут в отдельном потоке,
// Event Loop свободен для других запросов

Масштабирование: Cluster Module

// === Один процесс может использовать только один CPU core ===
// Для использования всех cores нужен Cluster

const cluster = require('cluster');
const os = require('os');
const http = require('http');

const numCores = os.cpus().length; // например, 8

if (cluster.isMaster) {
  // Главный процесс создаёт worker'ов
  for (let i = 0; i < numCores; i++) {
    cluster.fork(); // Создаёт отдельный процесс Node.js
  }
  
  console.log(`Master запущен. ${numCores} worker'ов готовы обрабатывать запросы`);
  
} else {
  // Каждый worker имеет свой Event Loop
  const server = http.createServer((req, res) => {
    res.writeHead(200);
    res.end(`Worker ${process.pid} обработал запрос\n`);
  });
  
  server.listen(3000);
  console.log(`Worker ${process.pid} слушает на порту 3000`);
}

// === Балансировка нагрузки ===
// Операционная система (OS) автоматически распределяет
// входящие соединения между worker'ами
// 10 клиентов могут быть распределены:
// Worker 1: 2 клиента
// Worker 2: 2 клиента
// Worker 3: 1 клиент
// и т.д.

Мониторинг памяти и производительности

// === Когда процесс начинает тормозить ===

const http = require('http');

const server = http.createServer((req, res) => {
  // Проверяем использование памяти
  const memUsage = process.memoryUsage();
  console.log(`Память используется: ${Math.round(memUsage.heapUsed / 1024 / 1024)} MB`);
  
  if (memUsage.heapUsed > 500 * 1024 * 1024) {
    // Если > 500MB, нужно оптимизировать или рестартовать
    console.warn('Критическое использование памяти!');
  }
  
  res.writeHead(200);
  res.end('OK');
});

server.listen(3000);

// === Graceful shutdown ===
server.on('clientError', (err, socket) => {
  if (err.code === 'ECONNRESET' || !socket.writable) {
    return;
  }
  socket.end('HTTP/1.1 400 Bad Request\r\n\r\n');
});

process.on('SIGTERM', () => {
  console.log('Получен сигнал SIGTERM, закрываем соединения...');
  server.close(() => {
    console.log('Сервер закрыт. Процесс завершается.');
    process.exit(0);
  });
});