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

Может ли быть заблокирован Event Loop?

2.2 Middle🔥 231 комментариев
#Node.js и JavaScript

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

🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)

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

Event Loop блокировка в Node.js

Да, Event Loop может быть заблокирован! Это одна из самых опасных и трудноуловимых проблем в Node.js.

Что такое Event Loop?

Event Loop — это сердце Node.js. Это одноточный механизм, который обрабатывает все события:

Новый запрос → Event Loop → Обработка → Ответ

Пока Event Loop занят — все остальные запросы ждут.

Как заблокировать Event Loop

1. Synchronous (синхронная) обработка данных

const express = require('express');
const app = express();

// ❌ ПЛОХО: синхронное вычисление
app.get('/compute', (req, res) => {
  // Этот цикл заблокирует Event Loop!
  let result = 0;
  for (let i = 0; i < 1_000_000_000; i++) {
    result += Math.sqrt(i);
  }
  res.json({ result });
});

app.listen(3000);

Что происходит:

  1. Запрос приходит
  2. Event Loop начинает считать (занят)
  3. Все остальные запросы ждут в очереди
  4. Если цикл занимает 5 секунд — все ждут 5 секунд!
Запрос 1: /compute          (занимает Event Loop 5 сек)
Запрос 2: /health (простой) (ждёт 5 сек, хотя должен выполниться за 1ms)
Запрос 3: /data   (простой) (ждёт 5 сек)

2. JSON парсинг больших объектов

app.post('/data', (req, res) => {
  // ❌ Если body огромный — парсинг заблокирует Event Loop
  const data = JSON.parse(req.body);
  res.json({ processed: true });
});

// Пример: 1GB JSON
// Парсинг может занять 10+ секунд
// Event Loop будет заблокирован на всё это время

3. Синхронные операции с файлами

const fs = require('fs');

app.get('/read', (req, res) => {
  // ❌ ОЧЕНЬ ПЛОХО: синхронное чтение
  const data = fs.readFileSync('large-file.bin');
  res.json({ size: data.length });
});

// Event Loop заблокирован на всё время чтения с диска!

4. Криптографические вычисления

const crypto = require('crypto');

app.post('/hash', (req, res) => {
  // ❌ Дорогостоящее вычисление
  const iterations = 100_000;
  const hash = crypto.pbkdf2Sync(
    req.body.password,
    'salt',
    iterations,  // 100K итераций = 2+ секунды
    64,
    'sha512'
  );
  res.json({ hash: hash.toString('hex') });
});

5. Регулярные выражения (ReDoS — Regular Expression Denial of Service)

// ❌ Опасное регулярное выражение
const regex = /(a+)+b/;
const maliciousInput = 'a'.repeat(30) + 'x';

app.get('/validate', (req, res) => {
  // Это может зависнуть на 10+ секунд!
  if (regex.test(maliciousInput)) {
    res.json({ valid: true });
  }
});

// Алгоритм переполняет себя и тратит экспоненциальное время

Симптомы заблокированного Event Loop

1. Простые запросы становятся медленными

const express = require('express');
const app = express();

// Очень быстрый endpoint
app.get('/health', (req, res) => {
  res.json({ status: 'ok' });
});

// Медленный endpoint
app.get('/heavy', (req, res) => {
  // Блокировка на 10 секунд
  for (let i = 0; i < 10_000_000_000; i++) {
    Math.sqrt(i);
  }
  res.json({ done: true });
});

app.listen(3000);

// Тестирование:
// curl http://localhost:3000/health  # В нормальное время: 1ms
// curl http://localhost:3000/heavy   # Запускается /heavy
// curl http://localhost:3000/health  # ЖДЁТ 10 секунд! Должно быть 1ms

2. Логирование отстаёт

const logger = require('winston');

app.get('/heavy', (req, res) => {
  logger.info('Started processing');  // Логируется с задержкой!
  
  // Синхронная операция
  for (let i = 0; i < 10_000_000_000; i++) {
    Math.sqrt(i);
  }
  
  logger.info('Finished processing');  // Логируется с задержкой!
  res.json({});
});

// Логи появляются не сразу, а с задержкой

3. Connections дропаются

Лоад балансер думает, что сервер дохлый (не отвечает долго).

Как избежать блокировки

1. Асинхронность везде

// ✅ ХОРОШО: async вычисления
app.get('/compute', async (req, res) => {
  // Выполнится в потоке, не блокируя Event Loop
  const result = await new Promise((resolve) => {
    setImmediate(() => {
      let sum = 0;
      for (let i = 0; i < 1_000_000_000; i++) {
        sum += Math.sqrt(i);
      }
      resolve(sum);
    });
  });
  res.json({ result });
});

2. Worker Threads для CPU-intensive операций

const { Worker } = require('worker_threads');
const path = require('path');

app.get('/compute', (req, res) => {
  const worker = new Worker(path.join(__dirname, 'worker.js'));
  
  worker.on('message', (result) => {
    res.json({ result });
  });
  
  worker.on('error', reject);
  worker.postMessage({ n: 1_000_000_000 });
});

// worker.js
const { parentPort } = require('worker_threads');
parentPort.on('message', ({ n }) => {
  let sum = 0;
  for (let i = 0; i < n; i++) {
    sum += Math.sqrt(i);
  }
  parentPort.postMessage(sum);
});

3. Асинхронные операции с файлами

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

// ✅ Асинхронное чтение
app.get('/read', async (req, res) => {
  const data = await fs.readFile('large-file.bin');
  res.json({ size: data.length });
});

// Event Loop не будет заблокирован!

4. Асинхронная криптография

const crypto = require('crypto').promises;

app.post('/hash', async (req, res) => {
  // ✅ Асинхронная версия
  const hash = await crypto.pbkdf2(
    req.body.password,
    'salt',
    100_000,
    64,
    'sha512'
  );
  res.json({ hash: hash.toString('hex') });
});

5. Безопасные регулярные выражения

const RE2 = require('re2');

// ❌ Опасно
const dangerousRegex = /(a+)+b/;

// ✅ Безопасно (использует RE2)
const safeRegex = new RE2('(a+)+b');

app.get('/validate', (req, res) => {
  // Гарантировано не зависнет
  const valid = safeRegex.test(req.body.input);
  res.json({ valid });
});

Monitoring блокировок Event Loop

1. Использование clinic.js

npm install -g clinic
clinic doctor -- node app.js
# Покажет блокировки Event Loop

2. Custom мониторинг

const blockDetector = setInterval(() => {
  const now = Date.now();
  setTimeout(() => {
    const delay = Date.now() - now - 100; // Ожидаемое = 100ms
    if (delay > 50) {
      console.warn(`Event Loop blocked for ${delay}ms`);
    }
  }, 100);
}, 1000);

3. Prometheus метрики

const promClient = require('prom-client');

const eventLoopDelay = new promClient.Histogram({
  name: 'event_loop_delay_ms',
  help: 'Event Loop delay in milliseconds'
});

const start = Date.now();
setImmediate(() => {
  const delay = Date.now() - start;
  eventLoopDelay.observe(delay);
});

Production примеры

Плохая функция (блокирует):

app.post('/process-image', (req, res) => {
  // ❌ Синхронная обработка 10MB изображения
  const image = sharp(req.file.buffer)
    .resize(1000, 1000)
    .jpeg({ quality: 80 })
    .toBufferSync();  // БЛОКИРУЕТ!
  
  res.json({ success: true });
});

Хорошая функция (асинхронная):

app.post('/process-image', async (req, res) => {
  // ✅ Асинхронная обработка
  const image = await sharp(req.file.buffer)
    .resize(1000, 1000)
    .jpeg({ quality: 80 })
    .toBuffer();  // НЕ блокирует
  
  res.json({ success: true });
});

Итого: ключевые моменты

  1. Event Loop может быть заблокирован синхронным кодом
  2. Блокировка на миллисекунды = все запросы ждут
  3. Признаки: простые запросы медленные, логирование отстаёт
  4. Решение: используй async/await, Worker Threads, или setImmediate
  5. Мониторинг: clinic.js, Prometheus, custom детекторы
  6. Главное правило: избегай синхронных операций, всё должно быть асинхронным

Even Loop — это фундамент Node.js производительности. Понимание его работы — ключ к написанию масштабируемых приложений.

Может ли быть заблокирован Event Loop? | PrepBro