singleflight

什么是singleflight

singleflight是go拓展同步库中实现的一种针对重复的函数调用抑制机制,也就是说一组相同的操作(函数),只有一个函数能被执行,执行后的结果返回给其他同组的函数。singleflight应该算是一种并发模型,非常适合当redis某个key缓存失效时,这个时候一堆请求去数据库来取数据然后更新redis缓存,如果我们使用singlefilght并发模型的话,那就是redis key失效的时候,一堆去数据库的请求只有一个能成功,它将更新该redis key的value值,同时把value值给其他相同请求。

singlefilght中的Group结构体

type Group struct {
	mu sync.Mutex       // 互斥锁保护 m
	m  map[string]*call // 存形同key的 函数
}

singleflight有三个对外api

func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool)
func (g *Group) DoChan(key string, fn func() (interface{}, error)) <-chan Result
func (g *Group) Forget(key string)

Group.DoGroup.DoChan功能类似,Do是同步返回,DoChan返回一个chan实现异步返回。 Group.Forget方法可以通知 singleflight 在map删除某个key,接下来对该key的调用就会直接执行方法而不是等待前面的函数返回。

使用singleflight

package main

import (
	"fmt"
	"github.com/go-redis/redis"
	"golang.org/x/sync/singleflight"
	"sync"
	"sync/atomic"
)

func main() {
	client := redis.NewClient(&redis.Options{
		Addr:     "127.0.0.1:6379"})

	var g singleflight.Group
	var callTimes int32 =0
	fakeGetData := func() int32 {//模拟去数据库取数据,记录被调用次数
		callTimes = atomic.AddInt32(&callTimes,1)
		return callTimes
	}

	wg := sync.WaitGroup{}
	for i:=0;i<10;i++ { //模拟10并发
		wg.Add(1)
		go func() {
			defer wg.Done()
			ret,err,_:= g.Do("wida", func() (i interface{}, e error) {
				num := fakeGetData()
				client.Set("wida",num,0)
				return num,nil
			})
			fmt.Println(ret,err)
		}()
	}
	wg.Wait()
	fmt.Printf("callTimes %d \n",callTimes)
	ret,_:=client.Get("wida").Int()
	fmt.Printf("redis value %d \n",ret)
}

运行结果

$ go run main.go
1 <nil>
1 <nil>
1 <nil>
1 <nil>
1 <nil>
1 <nil>
1 <nil>
1 <nil>
1 <nil>
1 <nil>
callTimes 1 
redis value 1 

我们看到10个并发请求,fakeGetData只被调用了一次,reids的值也被设置为1,10个请求拿到了相同的结果。

如果要使用DoChan的方式只需要稍微修改下

go func() {
			defer wg.Done()
			retChan:= g.DoChan("wida", func() (i interface{}, e error) {
				num := fakeGetData()
				client.Set("wida",num,0)
				return num,nil
			})
			ret := <- retChan
			fmt.Println(ret)
}()

总结

本小节介绍了go拓展同步库中singleflight(golang.org/x/sync/singleflight),以及介绍了singleflight使用方式和适合的场景。