实现原理
将从数据库中加载到的数据临时缓存到程序中,避免在同一时间再次从数据库中加载数据
用法
目录结构:
|—— singleflight/ // 根目录 ****|—— server/ ********|—— main.go // 使用了缓存的 http 服务器 ****|—— test/ ********|—— main.go // 模拟并发访问 ****|—— go.mod
singleflight/server/main.go:
package main import ( "errors" "fmt" "github.com/gin-gonic/gin" "golang.org/x/sync/singleflight" "math/rand" "strconv" "time" ) var sf singleflight.Group func main() { router := gin.Default() router.GET("/", func(c *gin.Context) { cacheKey := c.DefaultQuery("key", "") // 模拟从缓存中读取数据(缓存未命中) data, err := loadFromCache(cacheKey) if err != nil { // 利用 singleflight 减少数据库请求 value, err, _ := sf.Do(cacheKey, func() (interface{}, error) { // 模拟从数据库中加载数据(正常情况下该方法只会被调用一次,且结果会被 go程序缓存起来) data, err = loadFromDB() if err != nil { return nil, err } // 模拟写入缓存 setCache(cacheKey, data) return data, nil }) if err != nil { panic(err) } data = value.(string) } c.JSON(200, gin.H{ "data": data, }) }) fmt.Println("server run at: http://localhost:8080") router.Run() } func loadFromCache(key string) (string, error) { return "", errors.New("缓存未命中") } func setCache(key, data string) {} func loadFromDB() (string, error) { fmt.Println("load from database.") rand.Seed(time.Now().UnixNano()) randInt := rand.Intn(999999999) return strconv.Itoa(randInt), nil }
singleflight/test/main.go:
package main import ( "fmt" "io/ioutil" "net/http" "sync" ) func HttpGet(uri string) (string, error) { resp, err := http.Get(uri) if resp != nil { defer resp.Body.Close() } if err != nil { return "", err } else { if resp.StatusCode == 200 { bodyReader := resp.Body body, err := ioutil.ReadAll(bodyReader) if err != nil { return "", err } else { return string(body), nil } } else { return "", fmt.Errorf("服务器异常") } } } func main() { uri := "http://localhost:8080?key=cached-key" wg := sync.WaitGroup{} for i := 0; i < 1000; i++ { wg.Add(1) go func() { ret, err := HttpGet(uri) if err != nil { fmt.Println("http get error:", err) } else { fmt.Println(ret) } wg.Done() }() } wg.Wait() }
文档更新时间: 2024-04-20 10:57 作者:lee