2020/03/08 17:13:11 get key from database
2020/03/08 17:13:11 data
2020/03/08 17:13:11 get key from database
2020/03/08 17:13:11 data
2020/03/08 17:13:11 get key from database
2020/03/08 17:13:11 data
2020/03/08 17:13:11 get key from database
2020/03/08 17:13:11 data
2020/03/08 17:13:11 get key from database
2020/03/08 17:13:11 data
2020/03/08 17:13:11 get key from database
2020/03/08 17:13:11 data
2020/03/08 17:13:11 get key from database
2020/03/08 17:13:11 data
2020/03/08 17:13:11 get key from database
2020/03/08 17:13:11 data
2020/03/08 17:13:11 get key from database
2020/03/08 17:13:11 data
2020/03/08 17:13:11 get key from database
2020/03/08 17:13:11 data
import"golang.org/x/sync/singleflight"vargsingleflight.Group//获取数据
funcgetData(keystring)(string,error){data,err:=getDataFromCache(key)iferr==errorNotExist{//模拟从db中获取数据
v,err,_:=g.Do(key,func()(interface{},error){returngetDataFromDB(key)//set cache
})iferr!=nil{log.Println(err)return"",err}//TOOD: set cache
data=v.(string)}elseiferr!=nil{return"",err}returndata,nil}
执行结果如下,可以看得到只有一个请求进入的db,其他的请求也正常返回了值,从而保护了后端DB。
1
2
3
4
5
6
7
8
9
10
11
2020/03/08 17:18:16 get key from database
2020/03/08 17:18:16 data
2020/03/08 17:18:16 data
2020/03/08 17:18:16 data
2020/03/08 17:18:16 data
2020/03/08 17:18:16 data
2020/03/08 17:18:16 data
2020/03/08 17:18:16 data
2020/03/08 17:18:16 data
2020/03/08 17:18:16 data
2020/03/08 17:18:16 data
packagesingleflight// import "golang.org/x/sync/singleflight"
import"sync"// call is an in-flight or completed singleflight.Do call
typecallstruct{wgsync.WaitGroup// These fields are written once before the WaitGroup is done
// and are only read after the WaitGroup is done.
//val和err用来记录fn发放执行的返回值
valinterface{}errerror// forgotten indicates whether Forget was called with this call's key
// while the call was still in flight.
// 用来标识fn方法执行完成之后结果是否立马删除还是保留在singleflight中
forgottenbool// These fields are read and written with the singleflight
// mutex held before the WaitGroup is done, and are read but
// not written after the WaitGroup is done.
//dups 用来记录fn方法执行的次数
dupsint//用来记录DoChan中调用次数以及需要返回的数据
chans[]chan<-Result}// Group represents a class of work and forms a namespace in
// which units of work can be executed with duplicate suppression.
typeGroupstruct{musync.Mutex// protects m
mmap[string]*call// lazily initialized
}// Do executes and returns the results of the given function, making
// sure that only one execution is in-flight for a given key at a
// time. If a duplicate comes in, the duplicate caller waits for the
// original to complete and receives the same results.
// The return value shared indicates whether v was given to multiple callers.
func(g*Group)Do(keystring,fnfunc()(interface{},error))(vinterface{},errerror,sharedbool){g.mu.Lock()ifg.m==nil{g.m=make(map[string]*call)}//check map是否已经存在值
ifc,ok:=g.m[key];ok{c.dups++g.mu.Unlock()c.wg.Wait()returnc.val,c.err,true}c:=new(call)c.wg.Add(1)g.m[key]=cg.mu.Unlock()g.doCall(c,key,fn)returnc.val,c.err,c.dups>0}//执行fn方法,并且wg.Done
// doCall handles the single call for a key.
func(g*Group)doCall(c*call,keystring,fnfunc()(interface{},error)){c.val,c.err=fn()c.wg.Done()...}