Як реалізувати кешування в Express.js для покращення продуктивності?
Кешування в Express.js
Кешування зберігає часто запитувані дані в швидкому доступному шарі, щоб зменшити час відповіді та навантаження на сервер. Це одна з найефективніших оптимізацій продуктивності.
Типи кешування
1. Кешування в пам'яті (node-cache)
Найкраще підходить для розгортань на одному сервері:
js
const NodeCache = require('node-cache');
const cache = new NodeCache({ stdTTL: 300, checkperiod: 60 });
function cacheMiddleware(duration) {
return (req, res, next) => {
const key = req.originalUrl;
const cached = cache.get(key);
if (cached) {
return res.json(cached);
}
const originalJson = res.json.bind(res);
res.json = (body) => {
cache.set(key, body, duration);
originalJson(body);
};
next();
};
}
// Кеш на 5 хвилин
app.get('/api/products', cacheMiddleware(300), async (req, res) => {
const products = await db.query('SELECT * FROM products');
res.json(products);
});2. Кешування в Redis (Розподілене)
Найкраще підходить для розгортань на кількох серверах:
js
const Redis = require('ioredis');
const redis = new Redis(process.env.REDIS_URL);
function redisCacheMiddleware(ttl = 300) {
return async (req, res, next) => {
const key = `cache:${req.originalUrl}`;
try {
const cached = await redis.get(key);
if (cached) {
return res.json(JSON.parse(cached));
}
} catch (err) {
console.error('Помилка Redis:', err);
}
const originalJson = res.json.bind(res);
res.json = async (body) => {
try {
await redis.setex(key, ttl, JSON.stringify(body));
} catch (err) {
console.error('Помилка запису в Redis:', err);
}
originalJson(body);
};
next();
};
}
app.get('/api/products', redisCacheMiddleware(600), getProducts);3. HTTP заголовки кешування
Дозволяють браузерам і CDN кешувати відповіді:
js
// Cache-Control для публічного кешованого контенту
app.get('/api/products', (req, res) => {
res.set('Cache-Control', 'public, max-age=300'); // 5 хв
res.json(products);
});
// Без кешу для приватних даних
app.get('/api/profile', auth, (req, res) => {
res.set('Cache-Control', 'private, no-cache');
res.json(user);
});
// Кешування на основі ETag
const etag = require('etag');
app.get('/api/data', (req, res) => {
const data = getData();
const tag = etag(JSON.stringify(data));
if (req.headers['if-none-match'] === tag) {
return res.status(304).end(); // Не змінено
}
res.set('ETag', tag);
res.json(data);
});4. Інвалідація кешу
js
// Інвалідувати при операціях запису
app.post('/api/products', async (req, res) => {
const product = await createProduct(req.body);
// Інвалідувати пов'язані кеші
cache.del('/api/products');
await redis.del('cache:/api/products');
res.status(201).json(product);
});
// Інвалідація на основі шаблону з Redis
async function invalidatePattern(pattern) {
const keys = await redis.keys(pattern);
if (keys.length > 0) {
await redis.del(...keys);
}
}
// Інвалідувати всі кеші продуктів
await invalidatePattern('cache:/api/products*');Порівняння стратегій кешування
| Стратегія | Швидкість | Масштабованість | Постійність | Сценарій використання |
|---|---|---|---|---|
| В пам'яті | Найшвидша | Один сервер | Ні | Маленькі додатки, розробка |
| Redis | Дуже швидка | Багато серверів | Так | Продукційні API |
| HTTP заголовки | Клієнтська | Підходить для CDN | Браузер | Статичний контент |
| CDN | Найшвидша для користувачів | Глобальна | На краю | Активи, публічні API |
Що кешувати
| Кешувати | Не кешувати |
|---|---|
| Результати запитів до бази даних | Дані, специфічні для користувача (якщо не Redis) |
| Відповіді зовнішніх API | Дані в реальному часі (ціни акцій) |
| Обчислені/агреговані дані | Токени аутентифікації |
| Статичний контент | Точки з важкими записами |
Золоте правило: Кешуйте агресивно, інвалідуйте обережно. Починайте з коротких TTL (30-60с) і збільшуйте на основі частоти змін даних. Завжди майте спосіб примусово інвалідувати при зміні даних.
Коротка відповідь
Для співбесідиPremium
Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.