You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4.5 KiB
4.5 KiB
可降级缓存系统设计
日期: 2026-05-22 状态: approved
1. 概述
设计一个 Server-only 的通用 K-V 缓存系统 packages/cache,支持 Redis 主存储 + Memory 内存兜底。当 Redis 不可用时,自动降级到内存缓存,对调用方透明。
2. 架构
packages/cache/
├── package.json
├── index.ts ← 导出工厂函数和类型
└── lib/
├── types.ts ← 核心类型定义
├── managers/
│ └── cache-manager.ts ← CacheManager 实现
└── drivers/
├── base.ts ← Driver 基类 / 接口
├── memory-driver.ts ← 内存驱动
└── redis-driver.ts ← Redis 驱动(ioredis)
设计原则
- 分层适配器:调用方只跟
CacheManager交互,不感知底层存储 - 手动显式降级:按
drivers数组顺序尝试,返回第一个成功结果 - 扩展性:新增驱动(如
cloudflare-kv-driver)只需实现CacheDriver接口
3. 核心类型
// lib/types.ts
export interface CacheOptions {
ttl?: number // 默认 TTL(秒),0 = 不过期
namespace?: string // key 前缀隔离(未来扩展)
}
export interface CacheEntry<T = unknown> {
value: T
expiresAt: number | null // Unix ms,null = 永不过期
}
export interface CacheDriver {
name: string
get<T = unknown>(key: string): Promise<T | null>
set<T = unknown>(key: string, value: T, ttl?: number): Promise<void>
del(key: string): Promise<void>
exists(key: string): Promise<boolean>
clear(): Promise<void>
}
export interface CacheManagerOptions {
drivers: CacheDriver[]
defaultTtl?: number
}
4. CacheManager
文件: lib/managers/cache-manager.ts
export class CacheManager {
private drivers: CacheDriver[]
private defaultTtl: number
constructor(options: CacheManagerOptions)
async get<T = unknown>(key: string): Promise<T | null>
async set<T = unknown>(key: string, value: T, ttl?: number): Promise<void>
async del(key: string): Promise<void>
async exists(key: string): Promise<boolean>
async clear(): Promise<void>
withPrefix(ns: string): CacheManager
}
降级逻辑(get): 遍历 drivers,返回第一个成功结果,全部失败返回 null。
一致性逻辑(set/del/exists/clear): 遍历所有 driver 并发写入,确保降级后各层数据一致。
5. MemoryDriver
文件: lib/drivers/memory-driver.ts
- 用
Map<string, { value, expiresAt }>存储 - TTL = 0 不设置过期时间
- 过期检查在
get时懒清理(检查expiresAt是否已到) clear()清空整个 Map
6. RedisDriver
文件: lib/drivers/redis-driver.ts
- 使用
ioredis客户端 - value 序列化为
JSON.stringify存储 - TTL = 0 时使用
persist(不设过期) clear()使用FLUSHDB清空当前数据库(假设 prod 使用独立 db)- 需支持 disconnect 关闭连接
RedisOptions:
interface RedisOptions {
host: string
port: number
password?: string
db?: number
}
7. 工厂函数
文件: index.ts
interface CacheFactoryOptions {
redis?: {
host: string
port: number
password?: string
db?: number
}
memory?: boolean // 是否启用内存兜底,默认 true
defaultTtl?: number // 默认 TTL(秒)
}
function createCache(options: CacheFactoryOptions): CacheManager
行为:
- 如果指定了
redis,则将其作为主驱动加入 drivers 数组首位 - 如果
memory !== false,则将 MemoryDriver 加入 drivers 数组末位 - 如果既没有
redis也没有内存(memory: false),抛出错误
8. 使用方式
// 初始化(在 Nitro 插件或 server/utils 中
const cache = createCache({
redis: { host: '127.0.0.1', port: 6379 },
defaultTtl: 300,
})
// API 路由中使用
export default defineEventHandler(async (event) => {
const key = 'users:list'
const cached = await cache.get(key)
if (cached) return cached
const data = await fetchUsers()
await cache.set(key, data)
return data
})
9. 错误处理
get操作:单个 driver 失败时 warn 并尝试下一个;全部失败返回nullset/del/exists/clear操作:单个 driver 失败时 warn 但不中断(因为是并发写)- Redis 连接失败(如网络不可达):触发降级,不影响服务启动
10. 依赖
{
"ioredis": "^5.x"
}
内存驱动无外部依赖。