利用 CDN 缓存视图结果时,如何生成基于 ETag 的缓存键?
解读
在国内互联网架构中,CDN 边缘节点往往先于业务服务器命中流量。若直接把 CouchDB 视图(_view)URL 丢给 CDN,不做任何缓存键策略,会出现“同 URI 不同内容”或“内容已变却 304 失效”两大痛点:
- 视图是动态缩减的,同一 URI 在不同时间点可能返回全新行集;
- 默认情况下 CouchDB 只给文档附加 ETag,视图本身不会自动输出 ETag,导致 CDN 只能按 URI+QueryString 做键,无法感知数据版本。
因此,面试官想考察的是:你能否在不破坏 REST 语义、不改动 CouchDB 源码的前提下,为视图响应构造一枚可复用、可校验、分布式唯一的 ETag,并把它映射成 CDN 缓存键,实现毫秒级 304 回源降级。
知识点
- CouchDB 视图索引文件(.view)的签名机制:当 map/reduce 函数、数据库实例 UUID、分片副本集或行集发生任何变化,签名都会改变。
- 视图响应头ETag 规范:需满足
"<quote>opaque-token<quote>",且同一资源在字节一致时必须字节级相同。 - CDN 缓存键拼装顺序:scheme + host + path + query + 自定义变量(如 $etag_hash),国内云厂商(阿里云 CDN、腾讯云 ECDN、百度云 DCDN)均支持通过边缘规则把响应头值注入缓存键。
- 弱校验 vs 强校验:若索引更新频繁但行集顺序不变,可用弱 ETag(W/"…")减少回源;若金融场景要求强一致,则必须强 ETag。
- 分区库(CouchDB 3.x shards=true)场景下,每次写操作只落单个分区,需用全局合并签名(clustered signature)而非单节点签名,否则会出现“同一行集不同 ETag”导致 CDN 多份缓存。
答案
步骤一:在 CouchDB 外层部署极轻量反向层(OpenResty/Kong/Envoy),拦截 GET /{db}/_design/{ddoc}/_view/{view} 请求。
步骤二:反向层先本地拼装视图元信息字符串:
meta = db_uuid + '/' + ddoc_rev + '/' + view_sig + '/' + update_seq
其中 view_sig 通过内省端点 GET /{db}/_design/{ddoc}/_info 获取 "view_index.signature" 字段;update_seq 取数据库实例的集群级更新序号(/{db} 返回的 update_seq)。
步骤三:对 meta 字符串做** MurmurHash3 128bit** 并十六进制编码,得到 32 位小写摘要,作为强 ETag 值,写入响应头:
ETag: "7c3a1f8b…"
步骤四:在 CDN 控制台配置缓存键规则,把 $upstream_http_etag 变量追加到缓存键末尾,例如:
cache_key = $scheme$host$request_uri_$upstream_http_etag
步骤五:开启协商缓存,CDN 回源时带 If-None-Match,反向层再次计算实时 meta,若摘要一致直接返回 304,回源流量可降 95% 以上。
步骤六:若使用分片+副本,务必在每一层都读取集群全局 update_seq,避免副本延迟造成 ETag 漂移。
通过以上六步,即可在不改 CouchDB 源码、不侵入业务 SDK 的情况下,把视图结果安全地推至 CDN,实现毫秒级 304 与秒级失效。
拓展思考
- 离线优先场景:若终端 PouchDB 需先走 CDN 再回源 CouchDB,可把 ETag 直接作为本地
doc._rev的校验前缀,实现“离线缓存 + 增量同步”双保险。 - 多活数据中心:当北京、上海双集群同时写入,需用全局向量时钟(
clustered_update_seq)替代单点update_seq,否则跨地域 CDN 节点会出现** ETag 不一致**导致缓存雪崩。 - 函数即服务(FaaS)替代反向层:国内云厂商已支持边缘函数(阿里云 EdgeRoutine、腾讯云 SCF@Edge),可把上述 ETag 计算逻辑下沉到边缘,进一步减少 20~30 ms 回源延迟。
- 合规审计:金融与政企项目要求可验证缓存键,可把 ETag 生成算法写入《数据出口审计报告》,证明“缓存键一旦生成就与业务数据一一对应”,满足等保 2.0 对 NoSQL 审计的追踪要求。