17 changed files with 269 additions and 12 deletions
@ -0,0 +1,188 @@ |
|||||
|
# Cache System 使用文档 |
||||
|
|
||||
|
`packages/cache` 提供一个支持 Redis 主存储 + Memory 内存降级的通用 K-V 缓存系统。 |
||||
|
|
||||
|
## 安装 |
||||
|
|
||||
|
cache 包已作为 workspace 依赖安装,直接使用即可: |
||||
|
|
||||
|
```typescript |
||||
|
import { createCache } from 'cache' |
||||
|
``` |
||||
|
|
||||
|
## 快速开始 |
||||
|
|
||||
|
```typescript |
||||
|
import { createCache } from 'cache' |
||||
|
|
||||
|
// 初始化 |
||||
|
const cache = createCache({ |
||||
|
redis: { |
||||
|
host: '127.0.0.1', |
||||
|
port: 6379, |
||||
|
}, |
||||
|
defaultTtl: 300, // 默认 5 分钟 |
||||
|
}) |
||||
|
|
||||
|
// 在 Nitro 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 |
||||
|
}) |
||||
|
``` |
||||
|
|
||||
|
## API |
||||
|
|
||||
|
### createCache(options) |
||||
|
|
||||
|
创建 CacheManager 实例。 |
||||
|
|
||||
|
```typescript |
||||
|
interface CacheFactoryOptions { |
||||
|
redis?: RedisDriverOptions // Redis 配置,省略则只用内存 |
||||
|
memory?: boolean // 是否启用内存兜底,默认 true |
||||
|
defaultTtl?: number // 默认 TTL(秒),默认 0(永不过期) |
||||
|
} |
||||
|
|
||||
|
interface RedisDriverOptions { |
||||
|
host: string |
||||
|
port: number |
||||
|
password?: string |
||||
|
db?: number |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
**行为:** |
||||
|
- `redis` 配置存在时,Redis 作为主存储(优先尝试) |
||||
|
- `memory !== false` 时,Memory 作为兜底(Redis 不可用时自动降级) |
||||
|
- 两者都不配置则抛出错误 |
||||
|
|
||||
|
### CacheManager 实例方法 |
||||
|
|
||||
|
```typescript |
||||
|
// 读取缓存,未命中返回 null |
||||
|
get<T = unknown>(key: string): Promise<T | null> |
||||
|
|
||||
|
// 写入缓存 |
||||
|
// ttl 单位为秒,0 表示永不过期 |
||||
|
set<T = unknown>(key: string, value: T, ttl?: number): Promise<void> |
||||
|
|
||||
|
// 删除指定 key |
||||
|
del(key: string): Promise<void> |
||||
|
|
||||
|
// 检查 key 是否存在 |
||||
|
exists(key: string): Promise<boolean> |
||||
|
|
||||
|
// 清空所有缓存(谨慎使用) |
||||
|
clear(): Promise<void> |
||||
|
``` |
||||
|
|
||||
|
## 降级策略 |
||||
|
|
||||
|
采用**手动显式降级**:按驱动数组顺序尝试,返回第一个成功结果。 |
||||
|
|
||||
|
``` |
||||
|
RedisDriver (优先) |
||||
|
↓ 失败时 |
||||
|
MemoryDriver (兜底) |
||||
|
``` |
||||
|
|
||||
|
- `get` / `exists`:遍历 drivers,返回第一个成功结果 |
||||
|
- `set` / `del` / `clear`:并发写入/删除所有 drivers(保证各层数据一致) |
||||
|
- 单个 driver 失败时 warn 并继续,不影响整体操作 |
||||
|
|
||||
|
## 使用场景 |
||||
|
|
||||
|
### 场景一:纯内存缓存(开发/无 Redis 环境) |
||||
|
|
||||
|
```typescript |
||||
|
const cache = createCache({ |
||||
|
defaultTtl: 60, |
||||
|
}) |
||||
|
``` |
||||
|
|
||||
|
### 场景二:Redis + 内存降级(生产环境) |
||||
|
|
||||
|
```typescript |
||||
|
const cache = createCache({ |
||||
|
redis: { |
||||
|
host: process.env.REDIS_HOST!, |
||||
|
port: Number(process.env.REDIS_PORT ?? 6379), |
||||
|
password: process.env.REDIS_PASSWORD, |
||||
|
db: Number(process.env.REDIS_DB ?? 0), |
||||
|
}, |
||||
|
defaultTtl: 300, |
||||
|
}) |
||||
|
``` |
||||
|
|
||||
|
### 场景三:禁用内存兜底(Redis 专用) |
||||
|
|
||||
|
```typescript |
||||
|
const cache = createCache({ |
||||
|
redis: { host: '127.0.0.1', port: 6379 }, |
||||
|
memory: false, |
||||
|
}) |
||||
|
``` |
||||
|
|
||||
|
### 场景四:Nitro Plugin 全局注入 |
||||
|
|
||||
|
`server/plugins/cache.ts`: |
||||
|
|
||||
|
```typescript |
||||
|
import { createCache } from 'cache' |
||||
|
|
||||
|
export default defineNitroPlugin(() => { |
||||
|
const cache = createCache({ |
||||
|
redis: { |
||||
|
host: '127.0.0.1', |
||||
|
port: 6379, |
||||
|
}, |
||||
|
defaultTtl: 300, |
||||
|
}) |
||||
|
|
||||
|
// 注入到 H3 event context |
||||
|
event.context.cache = cache |
||||
|
}) |
||||
|
``` |
||||
|
|
||||
|
`server/api/users.get.ts`: |
||||
|
|
||||
|
```typescript |
||||
|
export default defineEventHandler(async (event) => { |
||||
|
const cache = event.context.cache |
||||
|
|
||||
|
const cached = await cache.get('users:list') |
||||
|
if (cached) return cached |
||||
|
|
||||
|
const data = await fetchUsers() |
||||
|
await cache.set('users:list', data) |
||||
|
return data |
||||
|
}) |
||||
|
``` |
||||
|
|
||||
|
## 数据结构 |
||||
|
|
||||
|
所有值以 JSON 序列化存储到 Redis: |
||||
|
|
||||
|
```typescript |
||||
|
await cache.set('user:1', { id: 1, name: 'Alice' }) |
||||
|
// Redis: SET 'user:1' '{"id":1,"name":"Alice"}' EX 300 |
||||
|
``` |
||||
|
|
||||
|
## 注意事项 |
||||
|
|
||||
|
- **TTL 单位是秒**,Redis `EX` 参数单位也是秒 |
||||
|
- `clear()` 在 Redis 上执行 `FLUSHDB`,会清空整个数据库,请确认 namespace 隔离 |
||||
|
- MemoryDriver 使用进程内存,重启进程后缓存丢失 |
||||
|
- 不支持存储 `undefined`、Symbol 或含循环引用的对象(JSON 序列化限制) |
||||
Binary file not shown.
@ -0,0 +1,22 @@ |
|||||
|
import { createCache } from 'cache' |
||||
|
|
||||
|
const cache = createCache({ |
||||
|
// redis: {
|
||||
|
// host: process.env.REDIS_HOST ?? '127.0.0.1',
|
||||
|
// port: Number(process.env.REDIS_PORT ?? 6379),
|
||||
|
// password: process.env.REDIS_PASSWORD,
|
||||
|
// db: Number(process.env.REDIS_DB ?? 0),
|
||||
|
// },
|
||||
|
// defaultTtl: 300,
|
||||
|
memory: true |
||||
|
}) |
||||
|
|
||||
|
if (import.meta.dev) { |
||||
|
console.log('plugin: 04.cache') |
||||
|
} |
||||
|
|
||||
|
export default defineNitroPlugin((nitroApp) => { |
||||
|
nitroApp.hooks.hook('request', async (event) => { |
||||
|
event.context.cache = cache |
||||
|
}) |
||||
|
}) |
||||
@ -0,0 +1,7 @@ |
|||||
|
import type { CacheManager } from 'cache' |
||||
|
|
||||
|
declare module 'h3' { |
||||
|
interface H3EventContext { |
||||
|
cache: CacheManager |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue