缓存策略 #
一、缓存概述 #
1.1 Hapi缓存系统 #
Hapi使用Catbox作为缓存系统,支持多种缓存后端:
- 内存缓存
- Redis
- Memcached
- MongoDB
- 自定义适配器
1.2 缓存类型 #
| 类型 | 说明 |
|---|---|
| 服务端缓存 | 服务器端数据缓存 |
| 方法缓存 | 服务器方法结果缓存 |
| 客户端缓存 | HTTP缓存头控制 |
二、配置缓存 #
2.1 内存缓存 #
javascript
const Hapi = require('@hapi/hapi');
const server = Hapi.server({
port: 3000,
cache: [{
name: 'memory',
provider: {
constructor: require('@hapi/catbox-memory'),
options: {
maxByteSize: 100 * 1024 * 1024
}
}
}]
});
2.2 Redis缓存 #
bash
npm install @hapi/catbox-redis
javascript
const CatboxRedis = require('@hapi/catbox-redis');
const server = Hapi.server({
port: 3000,
cache: [{
name: 'redis',
provider: {
constructor: CatboxRedis,
options: {
partition: 'my-app',
host: 'localhost',
port: 6379,
password: 'secret'
}
}
}]
});
2.3 多缓存配置 #
javascript
const server = Hapi.server({
port: 3000,
cache: [
{
name: 'memory',
provider: {
constructor: require('@hapi/catbox-memory'),
options: { maxByteSize: 50 * 1024 * 1024 }
}
},
{
name: 'redis',
provider: {
constructor: require('@hapi/catbox-redis'),
options: {
partition: 'my-app',
host: 'localhost',
port: 6379
}
}
}
]
});
三、方法缓存 #
3.1 基本缓存 #
javascript
server.method('getUser', async (id) => {
return await User.findById(id);
}, {
cache: {
expiresIn: 60 * 1000,
generateTimeout: 100
}
});
3.2 缓存选项 #
| 选项 | 说明 |
|---|---|
| cache | 缓存名称 |
| expiresIn | 过期时间(毫秒) |
| staleIn | 过期前刷新时间 |
| staleTimeout | 刷新超时 |
| generateTimeout | 生成超时 |
3.3 高级缓存配置 #
javascript
server.method('getProduct', async (id) => {
return await Product.findById(id);
}, {
cache: {
cache: 'redis',
expiresIn: 60 * 60 * 1000,
staleIn: 30 * 60 * 1000,
staleTimeout: 100,
generateTimeout: 1000
},
generateKey: (id) => `product:${id}`
});
3.4 缓存键生成 #
javascript
server.method('search', async (query, filters) => {
return await SearchService.search(query, filters);
}, {
cache: {
expiresIn: 60 * 1000
},
generateKey: (query, filters) => {
return `search:${query}:${JSON.stringify(filters)}`;
}
});
四、缓存操作 #
4.1 手动缓存 #
javascript
const cache = server.cache({
cache: 'redis',
segment: 'users',
expiresIn: 60 * 60 * 1000
});
server.route({
method: 'GET',
path: '/users/{id}',
handler: async (request, h) => {
const key = `user:${request.params.id}`;
let user = await cache.get(key);
if (!user) {
user = await User.findById(request.params.id);
await cache.set(key, user);
}
return user;
}
});
4.2 删除缓存 #
javascript
server.route({
method: 'PUT',
path: '/users/{id}',
handler: async (request, h) => {
const user = await User.update(request.params.id, request.payload);
const key = `user:${request.params.id}`;
await cache.drop(key);
return user;
}
});
4.3 清空缓存 #
javascript
server.route({
method: 'POST',
path: '/admin/clear-cache',
handler: async (request, h) => {
await cache.drop();
return { message: 'Cache cleared' };
}
});
五、客户端缓存 #
5.1 Cache-Control #
javascript
server.route({
method: 'GET',
path: '/static/{path*}',
handler: {
directory: {
path: './public'
}
},
options: {
cache: {
expiresIn: 30 * 24 * 60 * 60 * 1000,
privacy: 'public'
}
}
});
5.2 ETag #
javascript
server.route({
method: 'GET',
path: '/api/data',
handler: (request, h) => {
const data = { value: 'data' };
return h.response(data)
.etag('abc123');
}
});
5.3 Last-Modified #
javascript
server.route({
method: 'GET',
path: '/article/{id}',
handler: async (request, h) => {
const article = await Article.findById(request.params.id);
return h.response(article)
.header('Last-Modified', article.updatedAt.toUTCString());
}
});
六、缓存策略 #
6.1 分层缓存 #
javascript
server.method('getData', async (key) => {
return await fetchData(key);
}, {
cache: {
cache: 'memory',
expiresIn: 30 * 1000
}
});
server.method('getDataLongTerm', async (key) => {
return await fetchData(key);
}, {
cache: {
cache: 'redis',
expiresIn: 60 * 60 * 1000
}
});
6.2 条件缓存 #
javascript
server.method('getUser', async (id) => {
return await User.findById(id);
}, {
cache: {
expiresIn: 60 * 1000,
generateTimeout: 100
},
generateKey: (id) => {
if (id === 'admin') {
return null;
}
return `user:${id}`;
}
});
6.3 缓存预热 #
javascript
server.events.on('start', async () => {
const users = await User.findAll();
for (const user of users) {
await server.methods.getUser.cache.set(
`user:${user.id}`,
user
);
}
console.log('Cache warmed up');
});
七、缓存失效 #
7.1 基于时间 #
javascript
server.method('getSettings', async () => {
return await Settings.findAll();
}, {
cache: {
expiresIn: 60 * 1000,
staleIn: 30 * 1000,
staleTimeout: 100
}
});
7.2 基于事件 #
javascript
server.event('userUpdated');
server.events.on('userUpdated', async (userId) => {
await server.methods.getUser.cache.drop(userId);
});
server.route({
method: 'PUT',
path: '/users/{id}',
handler: async (request, h) => {
const user = await User.update(request.params.id, request.payload);
server.events.emit('userUpdated', request.params.id);
return user;
}
});
7.3 标签缓存 #
javascript
const cache = server.cache({
segment: 'products',
expiresIn: 60 * 60 * 1000
});
server.route({
method: 'GET',
path: '/products/{id}',
handler: async (request, h) => {
const key = `product:${request.params.id}`;
let product = await cache.get(key);
if (!product) {
product = await Product.findById(request.params.id);
await cache.set(key, product, 0, ['products']);
}
return product;
}
});
八、缓存监控 #
8.1 缓存统计 #
javascript
server.method('getCacheStats', async () => {
const stats = await cache.stats();
return {
hits: stats.hits,
misses: stats.misses,
sets: stats.sets,
deletes: stats.deletes
};
});
8.2 缓存健康检查 #
javascript
server.route({
method: 'GET',
path: '/health/cache',
handler: async (request, h) => {
try {
await cache.set('health-check', 'ok', 1000);
const value = await cache.get('health-check');
return {
status: value === 'ok' ? 'healthy' : 'unhealthy'
};
} catch (error) {
return {
status: 'unhealthy',
error: error.message
};
}
}
});
九、完整示例 #
9.1 缓存服务 #
javascript
const Hapi = require('@hapi/hapi');
const CatboxRedis = require('@hapi/catbox-redis');
const init = async () => {
const server = Hapi.server({
port: 3000,
cache: [{
name: 'redis',
provider: {
constructor: CatboxRedis,
options: {
partition: 'my-app',
host: 'localhost',
port: 6379
}
}
}]
});
const userCache = server.cache({
cache: 'redis',
segment: 'users',
expiresIn: 60 * 60 * 1000
});
server.method('getUser', async (id) => {
return await User.findById(id);
}, {
cache: {
cache: 'redis',
expiresIn: 60 * 60 * 1000,
generateTimeout: 100
},
generateKey: (id) => `user:${id}`
});
server.route([
{
method: 'GET',
path: '/users/{id}',
handler: async (request, h) => {
return await server.methods.getUser(request.params.id);
}
},
{
method: 'PUT',
path: '/users/{id}',
handler: async (request, h) => {
const user = await User.update(request.params.id, request.payload);
await server.methods.getUser.cache.drop(request.params.id);
return user;
}
}
]);
await server.start();
console.log('Server running on %s', server.info.uri);
};
init();
十、总结 #
缓存策略要点:
| 功能 | 说明 |
|---|---|
| 内存缓存 | @hapi/catbox-memory |
| Redis缓存 | @hapi/catbox-redis |
| 方法缓存 | server.method cache |
| 客户端缓存 | Cache-Control, ETag |
| 缓存失效 | drop, 事件 |
下一步,让我们学习安全最佳实践!
最后更新:2026-03-28