You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.30 04:54:05