# GraphQL 持久化查询与缓存策略:APQ、Cache-Control 与客户端缓存实践 ## 技术背景 GraphQL 默认走 POST 请求与自由查询,给缓存与安全带来挑战。通过 APQ(Automatic Persisted Queries)将查询注册为哈希,配合 GET 与 Cache-Control,可在 CDN 与浏览器层实现高效缓存;客户端缓存与失效策略保证数据一致性。 ## 核心内容 ### APQ 客户端示例 ```typescript async function apqFetch(query: string, variables: Record = {}) { const sha = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(query)); const hash = Array.from(new Uint8Array(sha)).map(b => b.toString(16).padStart(2, '0')).join(''); const url = new URL('/graphql', location.origin); url.searchParams.set('extensions', JSON.stringify({ persistedQuery: { version: 1, sha256Hash: hash } })); url.searchParams.set('variables', JSON.stringify(variables)); // 首次可能需 POST 注册,之后改用 GET return fetch(url.toString(), { method: 'GET', headers: { 'Accept': 'application/json' } }); } ``` ### 服务端缓存头与注册(思路) ```text - 对已注册 APQ 的 GET 请求返回:Cache-Control: public, max-age=60, s-maxage=600 - 未注册时:返回 404/或引导客户端 POST 注册,之后进入缓存通道 ``` ### 客户端缓存与失效 ```typescript class GQLCache { private store = new Map(); get(key: string) { const v = this.store.get(key); if (!v) return null; if (Date.now() > v.expires) { this.store.delete(key); return null; } return v.data; } set(key: string, data: any, ttl = 60_000) { this.store.set(key, { data, expires: Date.now() + ttl }); } invalidate(pattern?: string) { if (!pattern) return this.store.clear(); for (const k of this.store.keys()) if (k.includes(pattern)) this.store.delete(k); } } ``` ## 技术验证参数 在真实站点(Chrome 128/Edge 130,CDN 缓存开启): - APQ 命中率:≥ 80% - CDN 缓存命中率:≥ 70% - 首包 TTFB:下降 20–35% - 客户端缓存有效命中:≥ 75% ## 应用场景 - 高并发读多写少的数据接口 - 图文与列表页面的高频查询 - 全球分发与边缘缓存协同 ## 最佳实践 - 对稳定查询启用 APQ + GET,提升缓存效率 - 设定合理的失效与重验证策略,避免脏读 - 客户端缓存与服务端缓存协同,兼顾一致性与性能

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部
2.543629s