缓存装饰器需显式管理key生成、类型安全存取和上下文感知:keyFunc避免反射缺陷,CacheEntry封装类型信息,HTTP缓存须包含Query/Header/Authorization等维度,禁用defer清理以防竞态。

缓存装饰器必须自己管理 key 生成逻辑
Go 没有反射级的函数签名自动提取能力,func(x int, y string) bool 这类签名无法在运行时可靠转成可哈希的 key。硬编码 key 或用 fmt.Sprintf 拼接容易漏掉指针、结构体字段顺序、浮点 NaN 等边界情况。
- 推荐显式传入
keyFunc:接收原始参数,返回string或any(需可比较) - 避免对
struct{}、map、slice、func类型直接做fmt.Sprint—— 它们不可预测且不满足 map key 要求 - 简单场景可用
sprintf("%v-%v", a, b),但要确认a和b是基础类型或实现了稳定String()
用 sync.Map 实现线程安全缓存时注意 value 类型擦除
sync.Map 的 Load/Store 方法参数是 interface{},但 Go 不支持泛型自动推导,你得自己处理类型转换。一旦写错,运行时 panic 是常态,不是编译错误。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 缓存值建议封装为带类型信息的 struct,比如
type CacheEntry[T any] struct { Value T; ExpiresAt time.Time } - 别直接
m.Load(key).(MyType)—— 类型断言失败会 panic;改用if v, ok := m.Load(key).(MyType); ok { ... } - 如果缓存的是指针(如
*User),sync.Map存的是地址,多个 goroutine 修改同一对象仍需额外同步
HTTP handler 缓存装饰器要区分请求上下文
给 http.HandlerFunc 加缓存,不能只看 URL 路径。Query 参数、Header(如 Accept-Language)、Cookie、甚至 TLS 客户端证书都可能影响响应内容。
- 默认只缓存
r.URL.Path是危险的,/api/user?id=123和/api/user?id=456必须算不同 key - 用
r.URL.Query().Encode()拼进 key,但注意排序不稳定 —— 改用url.Values手动排序后再 encode - 涉及用户身份的接口,务必把
r.Header.Get("Authorization")或解析后的 user ID 加入 key,否则缓存污染
避免在 defer 中清理缓存导致竞态
有人想在 handler 结束后异步刷新缓存,于是写 defer cache.Delete(key)。问题在于:defer 在函数 return 后执行,而 HTTP handler 的 goroutine 可能已被复用,此时 cache 可能是另一个请求的实例,或者 key 已被覆盖。
- 缓存失效应同步触发,且在业务逻辑确认成功后立即调用,比如 DB 更新成功再
cache.Delete(key) - 不要依赖 defer 清理缓存;更不要在 defer 里起 goroutine 做异步删除 —— 没有上下文控制,容易泄漏
- 如果真需要延迟删除(比如防击穿),用
time.AfterFunc+ 显式 cancel 控制,而不是 defer
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










