GraphQL 持久化查询与缓存策略:APQ、Cache-Control 与客户端缓存实践技术背景GraphQL 默认走 POST 请求与自由查询,给缓存与安全带来挑战。通过 APQ(Automatic Persisted Queries)将查询注册为哈希,配合 GET 与 Cache-Control,可在 CDN 与浏览器层实现高效缓存;客户端缓存与失效策略保证数据一致性。核心内容APQ 客户端示例async function apqFetch(query: string, variables: Record<string, any> = {}) { 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' } }); } 服务端缓存头与注册(思路)- 对已注册 APQ 的 GET 请求返回:Cache-Control: public, max-age=60, s-maxage=600 - 未注册时:返回 404/或引导客户端 POST 注册,之后进入缓存通道 客户端缓存与失效class GQLCache { private store = new Map<string, { data: any; expires: number }>(); 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,提升缓存效率设定合理的失效与重验证策略,避免脏读客户端缓存与服务端缓存协同,兼顾一致性与性能

发表评论 取消回复