🧩 一、缓存的核心思路 在 Fastify + Next.js 场景下,缓存通常分为三层:
缓存层级 | 缓存内容 | 适用场景 |
---|---|---|
1. HTTP Header 缓存(浏览器/CDN 层) | 静态资源、HTML 页面 | CDN 前置缓存、浏览器缓存控制 |
2. 内存缓存(进程内) | 接口数据、配置、语言包、皮肤数据 | 小规模、高并发场景下的快速命中 |
3. 外部缓存(Redis 等) | 登录态、用户信息、接口响应 | 分布式服务场景,多节点共享 |
⚙️ 二、典型应用场景
✅ 常见你在自定义 Fastify 服务器中会缓存的内容包括:
/fePublicInfo
这样的公共接口(语言包、皮肤配置)- 登录用户的简要信息(token→user)
- SSR 数据预加载(例如首页接口)
- 远程配置文件、CDN JSON 等
🧠 三、Fastify 缓存的常用实现方式
方式一 :使用内存缓存(最简单) 使用 Map
或 lru-cache
作为本地缓存。
✅ 示例:缓存 /fePublicInfo
响应 10 分钟
kotlin
import Fastify from 'fastify'
import LRU from 'lru-cache'
const fastify = Fastify()
const cache = new LRU({
max: 100, // 最多缓存 100 条
ttl: 1000 * 60 * 10 // 缓存 10 分钟
})
fastify.get('/api/fePublicInfo', async (req, reply) => {
const cacheKey = 'fePublicInfo'
const cached = cache.get(cacheKey)
if (cached) {
// 命中缓存
reply.header('x-cache', 'HIT')
return cached
}
// 缓存未命中 → 请求远程服务
const res = await fetch('https://api.xxx.com/fePublicInfo')
const data = await res.json()
// 写入缓存
cache.set(cacheKey, data)
reply.header('x-cache', 'MISS')
return data
})
fastify.listen({ port: 3000 })
🔹 优点:超简单、适合小规模服务
🔹 缺点:内存缓存无法跨进程共享,适合单实例部署
方式二:使用 Redis 缓存(推荐生产环境) ✅ 示例:基于 Redis 缓存 API 响应
javascript
import Fastify from 'fastify'
import Redis from '@fastify/redis'
const fastify = Fastify()
fastify.register(Redis, { host: '127.0.0.1', port: 6379 })
fastify.get('/api/fePublicInfo', async (req, reply) => {
const cacheKey = 'fePublicInfo'
const cached = await fastify.redis.get(cacheKey)
if (cached) {
reply.header('x-cache', 'HIT')
return JSON.parse(cached)
}
const res = await fetch('https://api.xxx.com/fePublicInfo')
const data = await res.json()
// 缓存 10 分钟
await fastify.redis.set(cacheKey, JSON.stringify(data), 'EX', 60 * 10)
reply.header('x-cache', 'MISS')
return data
})
🔹 优点:跨实例共享、持久化、可限流
🔹 缺点:多一次网络开销
方式三:对 SSR 页面做缓存(全页缓存) 适用于不经常变化的 SSR 页面,比如首页或营销页。
✅ 示例:缓存 SSR 渲染结果
javascript
import Fastify from 'fastify'
import LRU from 'lru-cache'
import next from 'next'
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
const fastify = Fastify()
const pageCache = new LRU({ max: 100, ttl: 1000 * 60 * 5 }) // 5分钟缓存
fastify.get('/', async (req, reply) => {
const cacheKey = 'page:/'
if (pageCache.has(cacheKey)) {
reply.header('x-cache', 'HIT').type('text/html')
return reply.send(pageCache.get(cacheKey))
}
const html = await app.renderToHTML(req.raw, reply.raw, '/', req.query)
pageCache.set(cacheKey, html)
reply.header('x-cache', 'MISS').type('text/html')
reply.send(html)
})
fastify.all('*', (req, reply) => handle(req.raw, reply.raw))
🔹 优点:显著提升 SSR 页面性能
🔹 缺点:要注意缓存失效逻辑(如语言切换、登录态)
方式四:利用 HTTP 缓存头(CDN + 浏览器)
rust
reply.header('Cache-Control', 'public, max-age=300, stale-while-revalidate=60')
这告诉 CDN/浏览器:
- 正常缓存 300 秒;
- 超时后可继续用旧缓存 60 秒,同时异步更新。
综合最佳实践 在中大型项目中,推荐组合策略:
层级 | 缓存内容 | 技术 |
---|---|---|
CDN 层 | 图片、静态资源 | Cache-Control |
Fastify 层 | 公共接口 | Redis / LRU |
SSR 层 | 页面 HTML | LRU / Redis |
客户端层 | 用户操作数据 | localStorage / SW |
-
静态资源通过 CDN + Cache-Control 头;
-
接口数据通过 Fastify 插件(LRU 或 Redis)缓存;
-
SSR 页面通过 LRU 做全页缓存;
-
并结合 stale-while-revalidate 策略提升实时性。
这样既保证了性能,也避免缓存击穿问题。