GroupCache 需严格配置 peer 通信、GetterFunc 行为及 cacheBytes 硬上限,否则易因 singleflight 卡死或缓存不一致拖垮服务;GetterFunc 必须正确使用 ctx、禁止 goroutine 和外部状态修改、必须调用 dest.SetXXX();HTTPPool 需准确配置 BaseURL、可达 peer 列表和 Transport 超时;Sink 生命周期影响缓存写入;cacheBytes 是硬上限且无 TTL/CAS 机制。

GroupCache 不是“开箱即用”的共享缓存,它必须显式配置 peer 通信、严格约束 Getter 函数行为、并接受 cacheBytes 是硬上限——否则一上生产就可能因 singleflight 卡死或缓存不一致而拖垮整个微服务。
为什么 NewGroup 第三个参数(GetterFunc)写错会导致 Get 调用永久阻塞
GetterFunc 不是普通回调,它运行在 singleflight 的上下文中:所有同 key 的并发 Get 请求会被合并为一次调用。一旦这个函数违反契约,整个批次都会 hang 住。
-
ctx必须被实际使用:比如传给db.QueryRowContext(ctx, ...)或http.DefaultClient.Do(req.WithContext(ctx));漏掉则超时/取消失效,一个慢 DB 查询会让几十个 goroutine 一起卡死 - 不能启动 goroutine:singleflight 会等待函数返回,异步启动的协程无法被感知,dest 写入可能被忽略
- 不能修改外部状态:比如全局 map 或 channel,否则并发 Get 可能读到脏数据或 panic
- 必须调用
dest.SetXXX():哪怕只是dest.SetString(""),否则该次 Get 认为加载失败,下次仍会重试
HTTPPool 配置错误导致 peer 请求 404 或无限重试
GroupCache 节点间靠 HTTPPool 主动拉取数据,不是自动发现,URL 错配或 transport 缺失 timeout 就会出问题。
-
BaseURL必须和本机http.ListenAndServe(":8000", pool)监听地址完全一致,比如写成"http://127.0.0.1:8000"但服务监听在:9000,本地 miss 后发请求给自己却返回 404 -
pool.Set([]string{...})里的 peer 地址必须全部可达,GroupCache 默认不做健康检查,对已下线节点持续重试,拖慢整体响应 - 必须显式设置
Transport超时:默认http.Transport没 timeout,peer 请求可能卡住 30 秒以上,连带阻塞所有同 key 的 Get
正确写法示例:
在 Go 中使用 google/wire 实现编译时依赖注入——wire.NewSet、wire.Build、wire.Bind(接口→实现)、wire.Struct、wire.Value、wire.Interface
立即学习“go语言免费学习笔记(深入)”;
pool := &groupcache.HTTPPool{
BaseURL: "http://10.0.1.100:8000",
Transport: &http.Transport{
DialContext: (&net.Dialer{
Timeout: 500 * time.Millisecond,
KeepAlive: 30 * time.Second,
}).DialContext,
TLSHandshakeTimeout: 500 * time.Millisecond,
ResponseHeaderTimeout: 1 * time.Second,
},
}
pool.Set([]string{"http://10.0.1.101:8000", "http://10.0.1.102:8000"})Get 返回数据但没进缓存?检查 Sink 生命周期和类型
Get() 是否落本地缓存,不由 Group 决定,而由你传入的 Sink 实现控制——尤其注意 Sink 是否复用、是否被提前释放。
- 用
groupcache.AllocatingByteSliceSink(&data)时,data必须是局部变量且生命周期覆盖整个 Get 调用;若传入全局 slice 或已回收内存,写入后数据丢失 - 用
dest.SetString()时,字符串内容会被拷贝进 LRU;但若你实现自定义 Sink 并直接赋值指针(如dest.(*MySink).ptr = &val),后续修改原变量会污染缓存 - 缓存容量
cacheBytes是硬上限:设成64 << 20就真只用 64MB,超出部分按 LRU 淘汰,不会告警;设太小会导致热点 key 频繁进出,命中率骤降
最易被忽略的是:GroupCache 没有 TTL,没有主动刷新,也没有 CAS。它只保证“首次加载成功后,所有同 key 请求共享结果”,但不保证这个结果永远新鲜——如果底层数据变了,缓存不会自动失效,得靠业务层重建 Group 或手动清理。

















