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

Почему в 2024 году socket.io считается не лучшим выбором для WebSocket?

1.8 Middle🔥 121 комментариев
#API и сетевые протоколы#Фреймворки и библиотеки

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

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

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

Что такое libuv и какую роль она играет в Node.js?

libuv — это кроссплатформенная C библиотека, которая обеспечивает асинхронный I/O и Event Loop в Node.js. Это сердце Node.js.

Архитектура Node.js

┌─────────────────────────────────────┐
│       JavaScript (V8 engine)        │ ← Ваш код
└────────────────┬────────────────────┘
                 │
         ┌───────▼────────┐
         │  Node.js APIs  │ (fs, http, crypto, etc)
         └────────┬───────┘
                  │
         ┌────────▼────────────────┐
         │  libuv (Event Loop)     │ ← Асинхронный I/O
         ├──────────────────────────┤
         │ - File System            │
         │ - Network I/O            │
         │ - Timers                 │
         │ - Thread Pool            │
         └──────────────────────────┘

Основные компоненты libuv

1. Event Loop — ядро асинхронности

┌─────────────────────────────────────────────┐
│                Event Loop                   │
│                                             │
│  ┌────────────────────────────────────┐   │
│  │ 1. Timers (setTimeout, setInterval) │   │
│  │ 2. I/O callbacks (file ops)         │   │
│  │ 3. Check (setImmediate)             │   │
│  │ 4. Close callbacks                  │   │
│  └────────────────────────────────────┘   │
│                                             │
│  + Microtask Queue (Promise, nextTick)     │
└─────────────────────────────────────────────┘

2. Thread Pool — для blocking операций

// fs.readFile использует thread pool
fs.readFile('huge-file.txt', (err, data) => {
  // Чтение файла произошло в отдельном потоке
  console.log(data);
});

// Это НЕ блокирует main thread

По умолчанию 4 потока, настраивается через:

UV_THREADPOOL_SIZE=8 node app.js

3. I/O Polling — ожидание I/O событий

libuv проверяет, готовы ли файловые дескрипторы:
- Сокеты готовы читать?
- Файлы готовы писать?
- Таймеры истекли?

Как libuv управляет асинхронностью

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

// 1. setTimeout
setTimeout(() => {
  console.log('Timer callback');
}, 1000);

// 2. File I/O (использует thread pool)
fs.readFile('file.txt', (err, data) => {
  console.log('File read');
});

// 3. HTTP запрос
http.get('https://api.example.com', (res) => {
  console.log('HTTP response');
});

// libuv управляет всем этим:
// - Помещает setTimeout в очередь таймеров
// - Запускает readFile в thread pool
// - Регистрирует HTTP соединение
// - Event Loop проверяет, что готово
// - Вызывает колбэки

Event Loop в действии

const start = Date.now();

console.log('1');

setTimeout(() => {
  console.log('2 - setTimeout (1ms)');
  console.log(`Time elapsed: ${Date.now() - start}ms`);
}, 1);

Promise.resolve().then(() => {
  console.log('3 - Promise');
});

fs.readFile(__filename, () => {
  console.log('4 - fs.readFile');
});

console.log('5');

// Output:
// 1
// 5
// 3 - Promise
// 2 - setTimeout (1ms)
// 4 - fs.readFile

// Порядок выполнения:
// 1. Синхронный код (1, 5)
// 2. Microtask Queue (Promise = 3)
// 3. Timers phase (setTimeout = 2)
// 4. Poll phase (I/O = 4)

libuv обрабатывает разные ОС

Пороносимость благодаря libuv:

       Node.js код
            │
       ┌────▼────┐
       │ libuv   │
       └────┬────┘
        │   │   │
    ┌───▼─┬┴───┴─┬──┐
    │           │  │
Linux      macOS Windows
  (epoll)  (kqueue) (IOCP)

libuv скрывает разницу между операционными системами.

Практический пример: CPU-bound vs I/O-bound

// ✗ CPU-bound: блокирует Event Loop
function heavyComputation() {
  let sum = 0;
  for (let i = 0; i < 10**9; i++) {
    sum += i;
  }
  return sum;
}

// Это затвердит весь сервер!
const result = heavyComputation();

// ✓ I/O-bound: использует libuv thread pool
fs.readFile('huge-file.txt', (err, data) => {
  console.log(`Read ${data.length} bytes`);
  // Event Loop свободен, может обрабатывать другие запросы!
});

libuv в боевых ситуациях

Проблема 1: Истощение thread pool

// ✗ ПЛОХО: Много CPU-bound операций в одно время
for (let i = 0; i < 1000; i++) {
  crypto.pbkdf2(password, salt, 100000, 64, 'sha512', (err, derived) => {
    // thread pool перегружен!
  });
}

// ✓ ХОРОШО: Ограничить параллельность
const pLimit = require('p-limit');
const limit = pLimit(4);

const promises = passwords.map(pwd =>
  limit(() => hashPassword(pwd))
);

await Promise.all(promises);

Проблема 2: Забытые операции

// ✗ ПЛОХО: операция в thread pool, но мы не ждём
fs.readFile('file.txt', () => {});
process.exit(0); // Выход до завершения чтения

// ✓ ХОРОШО: Graceful shutdown
process.on('SIGTERM', async () => {
  console.log('Closing...');
  server.close();
  await db.close();
  process.exit(0);
});

Мониторинг libuv

const { performance, PerformanceObserver } = require('perf_hooks');

// Смотрим, как долго выполняются операции
const obs = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log(`${entry.name}: ${entry.duration.toFixed(2)}ms`);
  }
});

obs.observe({ entryTypes: ['measure'] });

performance.mark('fs-start');
fs.readFile('file.txt', () => {
  performance.mark('fs-end');
  performance.measure('fs', 'fs-start', 'fs-end');
});

Роль libuv в Node.js

  1. Event Loop — асинхронное управление
  2. Thread Pool — CPU-интенсивные операции
  3. I/O абстракция — файлы, сеть, pipes
  4. Кроссплатформенность — Windows, macOS, Linux
  5. Производительность — optimized для I/O операций

Вывод

libuv — это магический слой, который позволяет Node.js быть асинхронным и невероятно быстрым для I/O операций. Без него Node.js был бы простым синхронным интерпретатором JavaScript.