Как оценить быстродействие приложения на Node.js?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Оценка быстродействия Node.js приложения
Производительность — это не один метрик, а комплекс измерений. Расскажу о всех уровнях анализа:
1. Основные метрики производительности
Response Time (Latency):
- Время ответа на запрос
- Percentiles: p50, p95, p99 (более важны, чем average)
- Чем ниже, тем лучше
Throughput (RPS):
- Requests Per Second — сколько запросов в секунду обрабатываешь
- Растёт с оптимизацией
Memory Usage:
- Базовое потребление памяти
- Утечки (memory leaks)
- Garbage Collection паузы
CPU Usage:
- Сколько процессорных мощностей требует приложение
- Связано с Event Loop
2. Node.js встроенные tools
Простое логирование времени:
const startTime = Date.now();
await someAsyncOperation();
const duration = Date.now() - startTime;
console.log(`Operation took ${duration}ms`);
// ИЛИ более элегантно
console.time('operationName');
await someAsyncOperation();
console.timeEnd('operationName');
// Output: operationName: 1234ms
Встроенный profiler:
node --prof app.js
node --prof-process isolate-*.log > processed.txt
Inspector API (для real-time профилирования):
const inspector = require('inspector');
const session = new inspector.Session();
session.connect();
session.post('Profiler.enable', () => {
session.post('Profiler.start', () => {
// Твой код
setTimeout(() => {
session.post('Profiler.stop', (err, { profile }) => {
console.log(JSON.stringify(profile, null, 2));
session.disconnect();
});
}, 5000);
});
});
3. Нагрузочное тестирование
Apache Bench:
ab -n 10000 -c 100 http://localhost:3000/
wrk (профессиональный инструмент):
# 4 потока, 100 соединений, 30 секунд
wrk -t4 -c100 -d30s http://localhost:3000/
# С кастомным скриптом
wrk -t4 -c100 -d30s -s script.lua http://localhost:3000/
k6 (современный инструмент):
import http from 'k6/http';
import { check, sleep } from 'k6';
export let options = {
stages: [
{ duration: '2m', target: 100 }, // Рамп-ап
{ duration: '5m', target: 100 }, // Стабилизация
{ duration: '2m', target: 0 }, // Рамп-даун
],
};
export default function() {
let res = http.get('http://localhost:3000/');
check(res, {
'status is 200': (r) => r.status === 200,
'response time < 500ms': (r) => r.timings.duration < 500,
});
sleep(1);
}
4. Memory Profiling
Найти утечки памяти:
const v8 = require('v8');
const fs = require('fs');
// Снимок памяти в начале
const snapshot1 = v8.writeHeapSnapshot();
// После операции
await runOperations();
// Снимок памяти в конце
const snapshot2 = v8.writeHeapSnapshot();
// Анализируешь в Chrome DevTools
// chrome://inspect -> Memory -> Load heap snapshots
Мониторинг памяти:
const formatBytes = (bytes) => {
return (bytes / 1024 / 1024).toFixed(2) + ' MB';
};
setInterval(() => {
const usage = process.memoryUsage();
console.log('Memory Usage:');
console.log(` RSS: ${formatBytes(usage.rss)}`);
console.log(` Heap Total: ${formatBytes(usage.heapTotal)}`);
console.log(` Heap Used: ${formatBytes(usage.heapUsed)}`);
console.log(` External: ${formatBytes(usage.external)}`);
}, 5000);
5. CPU Profiling
Найти bottleneck:
node --prof app.js
node --prof-process isolate-*-v8.log > profile.txt
cat profile.txt | grep -A 5 'Summary'
Chrome DevTools для профилирования:
node --inspect-brk app.js
# Открываешь chrome://inspect
# Performance tab -> Record
6. APM Tools (для production мониторинга)
New Relic:
require('newrelic');
const express = require('express');
const app = express();
// New Relic автоматически будет профилировать
DataDog:
const tracer = require('dd-trace').init();
const express = require('express');
Prometheus + Grafana:
const promClient = require('prom-client');
const httpDuration = new promClient.Histogram({
name: 'http_request_duration_ms',
help: 'Duration of HTTP requests in ms',
buckets: [0.1, 5, 15, 50, 100, 500],
});
app.use((req, res, next) => {
const start = Date.now();
res.on('finish', () => {
const duration = Date.now() - start;
httpDuration.observe(duration);
});
next();
});
app.get('/metrics', async (req, res) => {
res.set('Content-Type', promClient.register.contentType);
res.end(await promClient.register.metrics());
});
7. Типичные проблемы и решения
Проблема 1: Event Loop блокируется
// ПЛОХО: синхронная операция
const data = fs.readFileSync('large-file.json');
// ХОРОШО: асинхронная
const data = await fs.promises.readFile('large-file.json');
// Мониторинг Event Loop lag
const lag = require('event-loop-lag');
console.log(`Event loop lag: ${lag()}ms`);
Проблема 2: N+1 queries
// ПЛОХО
const users = await User.find();
for (const user of users) {
user.posts = await Post.find({ userId: user.id });
}
// ХОРОШО
const users = await User.find().populate('posts');
Проблема 3: Неоптимизированные SQL queries
// Включи query logging
const queryDuration = new promClient.Histogram({
name: 'db_query_duration_ms',
help: 'Database query duration',
buckets: [10, 50, 100, 500, 1000],
});
db.on('query', (sql) => {
console.time(sql);
});
db.on('query-done', (sql, duration) => {
queryDuration.observe(duration);
if (duration > 1000) {
logger.warn('Slow query', { sql, duration });
}
});
8. Чеклист производительности
- Базовые метрики залогированы (response time, memory, CPU)
- Нагрузочное тестирование проведено
- Memory leaks найдены и исправлены
- Slow queries оптимизированы (с EXPLAIN)
- Event Loop не блокируется
- Используется caching (Redis для часто запрашиваемых данных)
- Connection pooling настроен
- Образы Docker оптимизированы
Итог
Производительность — это процесс, не событие. Измеряй постоянно, профилируй регулярно, мониторь в production. Маленькие оптимизации, сделанные на ранних стадиях, дают 10x результат.