Профилирование
▸Node.js Inspector
1node --inspect src/index.js
▸Chrome DevTools
Откройте chrome://inspect для профилирования Node.js приложения.
▸clinic.js
1npm install -g clinic2clinic doctor -- node src/index.js
Event Loop
▸Что такое Event Loop
Event Loop — цикл обработки событий в Node.js. Блокировка event loop замедляет всё приложение.
▸Измерение задержки
1const start = process.hrtime.bigint();2setImmediate(() => {3 const delay = Number(process.hrtime.bigint() - start) / 1e6;4 console.log(`Event loop delay: ${delay}ms`);5});
Оптимизация кода
▸Избегайте синхронных операций
1// Плохо2const data = fs.readFileSync('file.txt');34// Хорошо5const data = await fs.promises.readFile('file.txt');
▸Используйте stream для больших файлов
1// Плохо: загружает весь файл в память2const data = await fs.promises.readFile('large-file.csv');34// Хорошо: обрабатывает по частям5const stream = fs.createReadStream('large-file.csv');6stream.on('data', (chunk) => {7 // обработка чанка8});
▸Виртуальные машины
1// Изолируйте тяжёлые вычисления2const { VM } = require('vm2');3const vm = new VM();45const result = vm.run('1 + 1');
Кэширование
▸In-memory кэш
1const NodeCache = require('node-cache');2const cache = new NodeCache({ stdTTL: 300 });34function getCached(key, fetchFn) {5 let value = cache.get(key);6 if (value === undefined) {7 value = fetchFn();8 cache.set(key, value);9 }10 return value;11}
▸Redis кэш
1import { createClient } from 'redis';23const redis = createClient();45async function getCached(key, fetchFn, ttl = 3600) {6 const cached = await redis.get(key);7 if (cached) return JSON.parse(cached);89 const value = await fetchFn();10 await redis.set(key, JSON.stringify(value), { EX: ttl });11 return value;12}
Пул соединений
▸PostgreSQL
1const { Pool } = require('pg');23const pool = new Pool({4 max: 20,5 idleTimeoutMillis: 30000,6 connectionTimeoutMillis: 2000,7});
▸MongoDB
1mongoose.connect(uri, {2 maxPoolSize: 10,3 minPoolSize: 5,4});
Оптимизация запросов
▸N+1 проблема
1// Плохо: N+1 запросов2const users = await User.findAll();3for (const user of users) {4 user.posts = await Post.findByUserId(user.id);5}67// Хорошо: JOIN8const users = await User.findAll({9 include: [{ model: Post }],10});
▸Индексы
1// Создание индекса2await queryInterface.addIndex('Users', ['email'], { unique: true });34// Использование индекса5await User.findOne({ where: { email: 'test@test.com' } });
Оптимизация памяти
▸Утечки памяти
1node --max-old-space-size=4096 src/index.js
▸Профилирование памяти
1node --inspect src/index.js2# В Chrome DevTools: Memory tab → Take heap snapshot
▸Очистка
1// Очистка таймеров2const interval = setInterval(() => {}, 1000);3clearInterval(interval);45// Очистка listeners6const listener = () => {};7emitter.removeListener('event', listener);8``$910## Горизонтальное масштабирование1112### Cluster
import cluster from 'cluster';
import os from 'os';
if (cluster.isPrimary) {
const numCPUs = os.cpus().length;
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
} else {
// Запуск приложения
app.listen(3000);
}
12### PM2
npm install -g pm2
pm2 start src/index.js -i max
pm2 status
pm2 logs
12## HTTP сервер34### Keep-alive
const server = http.createServer(app);
server.keepAliveTimeout = 65000;
server.headersTimeout = 66000;
12### Compression
import compression from 'compression';
app.use(compression());
``$
Мониторинг
▸Prometheus + Grafana
1import client from 'prom-client';23const httpRequestDuration = new client.Histogram({4 name: 'http_request_duration_seconds',5 help: 'Duration of HTTP requests',6 labelNames: ['method', 'route', 'status_code'],7});89app.use((req, res, next) => {10 const end = httpRequestDuration.startTimer();11 res.on('finish', () => {12 end({ method: req.method, route: req.route?.path || 'unknown', status_code: res.statusCode });13 });14 next();15});
Load testing
▸Artillery
1npm install -g artillery2artillery quick --count 100 -n 50 http://localhost:3000/api/users
Best Practices
▸Используйте async/await
Избегайте callback hell и используйте промисы.
▸Ограничивайте параллелизм
1import pLimit from 'p-limit';23const limit = pLimit(10);4const tasks = urls.map(url => limit(() => fetch(url)));5await Promise.all(tasks);6``$78### Используйте worker threads
import { Worker } from 'worker_threads';
const worker = new Worker('./heavy-task.js');
worker.postMessage(data);
worker.on('message', (result) => {
console.log(result);
});