Go后端如何实现慢计算结果缓存并规避无效计算与请求阻塞
专有模式名称
这个场景对应的通用缓存策略有标准的工业界专有名称:stale-while-revalidate(旧值复用异步刷新),属于惰性缓存的优化变体,核心逻辑是缓存过期时不阻塞读请求,直接返回已有的陈旧值,同时后台异步触发缓存更新,完美匹配「无请求不计算、计算不阻塞读、周期内仅计算一次」的要求。
Go实现代码
package main import ( "encoding/json" "net/http" "sync" "time" ) var ( score interface{} updatedAt time.Time mu sync.Mutex updating bool // 标记是否有正在运行的计算任务 ) // 你的高开销计算函数,这里仅做占位 func calculateScoreExpensiveAndSlow() interface{} { // 模拟计算耗时 time.Sleep(2 * time.Second) return time.Now().Unix() } func getCachedScore() interface{} { mu.Lock() // 1. 缓存未过期,直接返回当前值 if time.Since(updatedAt) < 5*time.Second { s := score mu.Unlock() return s } // 2. 缓存已过期,但已经有计算任务在跑,直接返回旧值 if updating { s := score mu.Unlock() return s } // 3. 缓存过期且无计算任务,标记更新状态后立即解锁,不阻塞后续读请求 updating = true oldScore := score mu.Unlock() // 异步后台执行计算,不阻塞当前请求 go func() { newScore := calculateScoreExpensiveAndSlow() mu.Lock() score = newScore updatedAt = time.Now() updating = false mu.Unlock() }() // 冷启动处理:首次请求无旧值时等待计算完成再返回,不需要可删除这段逻辑 if oldScore == nil { for { mu.Lock() s := score done := !updating mu.Unlock() if done { return s } time.Sleep(10 * time.Millisecond) } } // 直接返回旧值 return oldScore } func serveScore(w http.ResponseWriter, r *http.Request) { b, _ := json.Marshal(getCachedScore()) w.Write(b) } func main() { http.HandleFunc("/score", serveScore) http.ListenAndServe(":8080", nil) }
实现特性说明
- 完全惰性触发:无客户端请求时不会执行任何计算,仅在有请求且缓存过期时才会触发计算
- 读请求无阻塞:计算过程全程在后台异步执行,所有读请求直接返回当前可用值,不会被计算过程阻塞
- 无重复计算:通过
updating标记确保同一时间最多只有一个计算任务运行,5秒缓存周期内最多执行一次计算 - 兼容冷启动场景:首次请求无旧值时可选择等待计算完成后返回,也可根据业务需要调整为返回默认值
内容的提问来源于stack exchange,提问作者xakepp35
相关产品推荐
相关产品推荐

