Express.jsSeniorSystem design
Каковы лучшие практики производительности для продакшн Express-приложений?
Production-производительность Express достигается через NODE_ENV=production, gzip-сжатие, кеширование через Redis, кластеризацию всех CPU ядер, HTTP keep-alive, исключение синхронного кода в hot path и профилирование event loop.
Производительность Express в продакшне
1. NODE_ENV=production
Это не просто переменная — Express читает её для включения оптимизаций: кеш шаблонов, сокращённые stack traces, отключение дорогих debug-операций. Всегда устанавливать явно.
NODE_ENV=production node server.js
2. Сжатие ответов
import compression from 'compression';
// Только для текстовых ответов > 1KB
app.use(compression({
threshold: 1024,
filter: (req, res) => {
if (req.headers['x-no-compression']) return false;
return compression.filter(req, res);
},
}));
3. Кластеризация
Один Node.js процесс использует одно ядро CPU. Для многоядерных серверов нужен cluster или PM2:
// cluster.js
import cluster from 'cluster';
import { cpus } from 'os';
import { createServer } from './server.js';
if (cluster.isPrimary) {
const numCPUs = cpus().length;
console.log(`Primary ${process.pid} running, forking ${numCPUs} workers`);
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', (worker) => {
console.log(`Worker ${worker.process.pid} died, restarting...`);
cluster.fork();
});
} else {
createServer();
}
# ecosystem.config.yml (PM2)
apps:
- name: api
script: src/server.js
instances: max
exec_mode: cluster
env_production:
NODE_ENV: production
4. HTTP Keep-Alive и таймауты
const server = app.listen(PORT);
// Keep-alive для повторного использования соединений
server.keepAliveTimeout = 65_000; // > ALB idle timeout (60s)
server.headersTimeout = 66_000; // > keepAliveTimeout
5. Кеширование через Redis
import { createClient } from 'redis';
const redis = createClient({ url: process.env.REDIS_URL });
await redis.connect();
const cacheMiddleware = (ttlSeconds) => async (req, res, next) => {
const key = `cache:${req.originalUrl}`;
const cached = await redis.get(key);
if (cached) {
res.setHeader('X-Cache', 'HIT');
return res.json(JSON.parse(cached));
}
const originalJson = res.json.bind(res);
res.json = async (data) => {
await redis.setEx(key, ttlSeconds, JSON.stringify(data));
res.setHeader('X-Cache', 'MISS');
originalJson(data);
};
next();
};
app.get('/api/products', cacheMiddleware(300), productsController.list);
6. Оптимизация работы с БД
- Connection pooling с правильным
max(обычно 10–20 на процесс). - Индексы на колонки WHERE/JOIN — одна плохая запись может убить весь сервис.
- Paginated queries вместо
SELECT *для больших таблиц. - Prepared statements для повторяющихся запросов.
7. Мониторинг event loop lag
import { monitorEventLoopDelay } from 'perf_hooks';
const h = monitorEventLoopDelay({ resolution: 20 });
h.enable();
setInterval(() => {
const lagMs = h.mean / 1e6;
if (lagMs > 50) {
logger.warn({ lagMs }, 'High event loop lag detected');
}
h.reset();
}, 10_000);
8. Статические файлы
Никогда не отдавать статику через Express в продакшне. Использовать nginx, CDN или отдельный S3/MinIO. Express добавляет overhead Node.js на каждый файл.
Подводные камни
- Устанавливать
compression()после роутов — middleware применяется только к запросам, зарегистрированным после него в цепочке. - Слишком маленький пул соединений: при 4 воркерах и
max: 20PostgreSQL получит 80 соединений — проверяйтеmax_connectionsна стороне БД. - Кешировать ответы с персонализированными данными (по user_id) без учёта ключа кеша — утечка данных между пользователями.
- Забывать инвалидировать кеш при мутациях — клиенты видят устаревшие данные.
keepAliveTimeoutменьше idle timeout load balancer (AWS ALB = 60s по умолчанию) — ALB разрывает соединения с ошибкой 502.- JSON.stringify на гигантских объектах в hot path блокирует event loop — профилировать с
clinic.js. - Синхронный
console.logв production — использоватьpinoс async transport.
Common mistakes
- Дает общий ответ про Node.js и не называет конкретные API Express.js.
- Не объясняет, где в lifecycle находится производительность Express в продакшене.
- Не разделяет validation, authorization, business logic и persistence.
- Игнорирует ошибки, лимиты входных данных, observability и тестирование.
What the interviewer is testing
- Может объяснить производительность Express в продакшене на примере кода.
- Называет ключевые API: NODE_ENV=production, compression, reverse proxy.
- Использует точные API Express.js, а не вымышленные hooks/decorators/methods.
- Видит production-риски: безопасность, отказоустойчивость, логирование и тесты.