AutoGPT 前端性能实践:Next.js 跨请求 LRU 缓存(server-cache-lru 规则详解)
本文围绕 AutoGPT 仓库内置的 Vercel React 最佳实践规则 server-cache-lru 展开,讲解当 React.cache() 的缓存范围只限于单个请求时,如何用 lru-cache 实现跨请求的数据缓存。读完本文,你可以掌握 LRU 缓存在 Next.js 服务端(Route Handler / Server Component)中的完整落地方式、max 与 ttl 参数的含义,以及在不同运行时环境(共享函数实例 vs 传统 Serverless)下的选型依据。
规则定位:Vercel React 最佳实践中的 server-cache-lru
该规则文件位于 .claude/skills/vercel-react-best-practices/rules/server-cache-lru.md,是 Vercel 工程团队维护的 45 条 React/Next.js 性能优化规则之一,元数据标注如下:
- title: Cross-Request LRU Caching
- impact: HIGH(高影响)
- impactDescription: caches across requests(跨请求缓存)
- tags: server, cache, lru, cross-request
在 SKILL.md 的规则优先级体系中,它归属于第 3 优先级分类「Server-Side Performance(服务端性能,HIGH)」,与以下 4 条服务端规则并列:
| 规则 | 一句话说明 |
|---|---|
server-cache-react |
用 React.cache() 做单请求内去重 |
server-cache-lru |
用 LRU 缓存做跨请求缓存 |
server-serialization |
最小化 RSC 边界序列化的数据量 |
server-parallel-fetching |
重组组件结构以并行化数据获取 |
server-after-nonblocking |
用 after() 执行不阻塞响应的操作 |
其中 server-cache-react 与 server-cache-lru 是一对互补规则:前者解决「一个请求内重复查询」,后者解决「多个请求间重复查询」。两者配合,构成 Next.js 服务端数据获取的完整缓存策略。
核心问题:React.cache() 的生命周期只有单个请求
规则原文点明了问题的本质:
React.cache()only works within one request. For data shared across sequential requests (user clicks button A then button B), use an LRU cache.
React.cache() 的去重范围被绑定在单次请求的生命周期内。参考同目录的 server-cache-react.md,它的典型用法是:
import { cache } from 'react'
export const getCurrentUser = cache(async () => {
const session = await auth()
if (!session?.user?.id) return null
return await db.user.findUnique({
where: { id: session.user.id }
})
})
在同一次渲染中,多个组件调用 getCurrentUser() 只会执行一次查询,这是它擅长的场景。但考虑用户交互的真实节奏:用户先点击按钮 A(触发请求 1),紧接着点击按钮 B(触发请求 2)。这两个是顺序执行的独立请求,React.cache() 在请求 1 结束后即被丢弃,请求 2 到达时数据必须重新查库。而这两次点击往往在秒级间隔内命中不同端点、却需要同一份数据——这正是 LRU 缓存要填补的空白。
两种缓存的分工可以概括为:
| 维度 | React.cache() |
LRU Cache(lru-cache) |
|---|---|---|
| 作用域 | 单个请求内 | 跨请求(进程/函数实例内) |
| 容量控制 | 无,随请求销毁 | max 条数上限,自动淘汰 |
| 时效控制 | 请求结束即失效 | ttl 过期时间 |
| 适用场景 | 同一次渲染内的多次调用去重 | 用户在短时间内连续操作命中同一份数据 |
LRU 缓存的完整实现
规则文档给出的参考实现如下(完整继承自 server-cache-lru.md):
import { LRUCache } from 'lru-cache'
const cache = new LRUCache<string, any>({
max: 1000, // 最多缓存 1000 个条目,超出后按 LRU 策略淘汰最久未访问项
ttl: 5 * 60 * 1000 // 每条缓存存活 5 分钟(毫秒),到期自动失效
})
export async function getUser(id: string) {
const cached = cache.get(id)
if (cached) return cached
const user = await db.user.findUnique({ where: { id } })
cache.set(id, user)
return user
}
// Request 1: DB query, result cached
// Request 2: cache hit, no DB query
几个关键参数与实现细节值得展开:
max: 1000:缓存容量上限。LRU(Least Recently Used,最近最少使用)策略保证超限时淘汰的是最久未被访问的条目,高频数据得以保留。条目数应结合单条数据的内存大小与实例内存上限权衡——本示例按 1000 条用户记录估算。ttl: 5 * 60 * 1000:lru-cache的 TTL 以毫秒为单位。5 分钟的时效与规则建议的使用场景(“within seconds”)匹配:既覆盖用户连续操作的窗口,又限制脏数据暴露时间。注意 TTL 到期后条目在下次get时判定失效,不会主动回调清除。- 泛型
LRUCache<string, any>:键为用户 ID 字符串,值为用户对象。生产环境建议将值类型收紧为具体实体类型,避免any掩盖序列化/字段变更问题。 - 模块级单例:
const cache = new LRUCache(...)必须声明在模块顶层,使同一函数实例内的所有请求共享同一个缓存对象;若放进函数体内,每次调用都会新建空缓存,规则即失效。 - 读写模式:先
get命中即返回,未命中再查库并set回填。这一「旁路缓存(Cache-Aside)」模式与数据库查询逻辑解耦,可平滑降级为直连数据库。
适用场景判定
规则给出的使用判据是一句话:当用户的顺序操作在数秒内命中多个端点、而这些端点需要同一份数据时,使用 LRU 缓存。典型的如:
- 请求 1(页面加载)查询用户资料,结果写入缓存;
- 用户在数秒内触发请求 2(按钮点击 / 下拉刷新 / 局部 RSC 更新),同一
getUser(id)直接从缓存返回,省去数据库往返。
反过来,若两次请求间隔远超 TTL、或数据本身要求强一致(如余额扣减后立即展示),则不应依赖该缓存层,或应将 TTL 收紧到业务可接受的窗口。
运行时环境影响:共享实例 vs 传统 Serverless
规则文档特别区分了两种部署形态下 LRU 缓存的有效性,这是选型时必须确认的前提:
共享函数实例环境(如 Vercel 的 Fluid Compute)
LRU 缓存在此尤为有效:多个并发请求可以复用同一个函数实例,模块顶层的 cache 对象因此在请求间持续存活——无需 Redis 等外部存储即可获得跨请求缓存。缓存的命中率直接取决于实例的复用密度。
传统 Serverless 环境
每次调用(invocation)在相互隔离的环境中运行,模块级缓存在冷启动后随实例销毁,跨进程无法共享。此时文档建议:考虑引入 Redis 等外部存储做跨进程缓存,否则进程内 LRU 只能在单实例的复用窗口内起作用,效果大打折扣。
从源码结构看,判断标准很清晰:只要你的运行时会把多个请求调度到同一进程/实例(长驻容器、Fluid Compute、本地开发服务器),进程内 LRU 就能稳定命中;若每次请求都是独立冷实例,则必须把缓存外置。
仓库佐证:AutoGPT 前端与 lru-cache 生态
- AutoGPT 平台的前端 autogpt_platform/frontend/package.json 使用 Next.js 15.5.21,属于 RSC(React Server Components)架构,正是
server-cache-react与server-cache-lru这类服务端规则的目标环境。 - 前端锁文件 autogpt_platform/frontend/pnpm-lock.yaml 中可见
lru-cache(10.4.3 / 11.2.4 / 5.1.1 等多个版本)作为 pnpm 生态的传递依赖被广泛引用,印证了lru-cache是 Node.js 生态中事实标准的进程内缓存实现,与规则推荐一致。 - 本仓库当前并未在前端业务代码中直接实例化
LRUCache,该规则文件(连同同目录 44 条规则)的定位是指导后续开发与重构:当你在 AutoGPT 前端新增需要跨请求共享数据的服务端函数(如按 ID 取用户、图、凭据的查询封装)时,可直接套用本文模式。
小结
React.cache()负责请求内去重(MEDIUM 影响),lru-cache负责跨请求缓存(HIGH 影响),两者按作用域分工,可叠加使用;- 落地三要素:模块顶层创建
LRUCache实例、max控制容量、ttl控制时效(毫秒); - 适用判据:用户顺序操作在数秒内重复命中同一数据;
- 环境前提:共享函数实例下进程内缓存即可,传统 Serverless 下需评估是否外置 Redis。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00