Диагностика замедления Node.js API является одной из наиболее распространенных задач при работе с приложениями на этом стеке технологий. Когда ваш API был быстрым при запуске, но через несколько месяцев latency начал расти, и вы не знаете, где искать проблему, пора прибегнуть к систематическому подходу для выявления причин.
Начало с Event Loop
Event Loop является сердцем Node.js, и когда он стопорится, все остальное также стопорится, не выдавая никаких ошибок или логов, просто увеличивая latency. Первым шагом должно быть измерение Event Loop. Это можно сделать с помощью модуля perf_hooks и его функции monitorEventLoopDelay. Эта функция позволяет измерить задержку Event Loop и получать статистику о времени, которое тратится на обработку каждого цикла.
const { monitorEventLoopDelay } = require('perf_hooks');
const histogram = monitorEventLoopDelay({ resolution: 10 });
histogram.enable();
setInterval(() => {
const p99 = histogram.percentile(99) / 1e6; // nanoseconds to ms
const p50 = histogram.percentile(50) / 1e6;
console.log(`Event loop delay — p50: ${p50.toFixed(2)}ms, p99: ${p99.toFixed(2)}ms`);
histogram.reset();
}, 5000);
Если значение p99 Event Loop постоянно выше 10ms, это означает, что что-то блокирует цикл, скорее всего, это связано с синхронной работой CPU в горячих точках. Общие виновники включают в себя:
JSON.parse/JSON.stringifyна больших объектах в обработчиках запросов- Синхронные криптографические операции (например,
bcrypt.hashSync,crypto.pbkdf2Sync) - Синхронные чтения файлов (
fs.readFileSyncвнутри обработчика запроса) - Тяжелые совпадения регулярных выражений на строках, предоставленных пользователем
- Рекурсивные операции, которые были быстрыми на малых данных, но плохо масштабируются
Профилирование CPU под нагрузкой
Если Event Loop выглядит здоровым (p99 меньше 5ms), то проблема лежит где-то еще. Для выявления CPU-интенсививных участков кода под нагрузкой можно использовать инструмент clinic.js. Это самый надежный способ найти CPU-горячие точки в условиях, похожих на производственные.
npm install -g clinic
clinic flame -- node server.js
Пока clinic работает, отправьте представительную нагрузку на сервер, используя инструменты типа autocannon или k6. Затем откройте график пламени (flame graph). Ищите широкие, плоские блоки вверху — это функции, которые потребляют непропорционально много времени CPU. График пламени показывает, где тратится время, и когда у вас есть имя функции, вы можете понять, почему она тратит так много времени.
Функция, которая выполняет работу за 2ms в среднем, может быть незаметна во время профилирования, но если она вызывается часто, ее влияние на общую производительность может быть значительным. Анализируя график пламени и понимая, какие функции потребляют больше всего времени, вы можете приступить к оптимизации и устранению проблем с производительностью в вашем Node.js API.