10.go开源groupcache项目蛤蟆笔记——singleFlight
1 singleFlight
这个包主要实现了一个可合并操作的接口
1.1 先定义一个回调函数结构call
type call struct {
//
类似
java
的
CountdownLatch
的
wg sync.WaitGroup
//
回调函数
val interface{}
// error
err error
}
1.2 定义一个Group结构体
type Group struct {
musync.Mutex // protects m
m map[string]*call // lazily initialized
}
Go使用开头字母大小写判断是否外部可见,大写外部可见,小写外部不可见,比如上面的Group在外部实用singleflght包是可以访问到的,而call不能.
1.3 Do函数
包的主要接口,用于向其他节点发送查询请求时,合并相同key的请求,减少热点可能带来的麻烦,比如说我请求key="123"的数据,在没有返回的时候又有很多相同key的请求,而此时后面的没有必要发,只要等待第一次返回的结果即可.
返回是一个inferace和error.入参是一个key和一个函数。
func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
//
首先获取
group
锁
g.mu.Lock()
//
映射表不存在就创建
1
个
if g.m == nil {
g.m = make(map[string]*call)
}
//
判断要查询某个
key
的请求是否已经在处理了
if c, ok := g.m[key]; ok {
//
已经在处理了
就释放
group
锁
并
wait
在
wg
上,我们看到
wg
加
1
是在某个时间段内第一次请求时加的
//
并且在完成
fn
操作返回后会
wg done
,那时
wait
等待就会返回,直接返回第一次请求的结果
g.mu.Unlock()
c.wg.Wait()
return c.val, c.err
}
//
如果没有在处理,则创建回调,
wg
加
1
,把回调存到
m
中表示已经有在请求了,释放锁
c := new(call)
c.wg.Add(1)
g.m[key] = c
g.mu.Unlock()
//
执行
fn
,释放
wg
c.val, c.err = fn()
c.wg.Done()
//
加锁将请求从
m
中删除,表示请求已经做好了
g.mu.Lock()
delete(g.m, key)
g.mu.Unlock()
return c.val, c.err
}
2 Singleflight_test
2.1 TestDo
定义一个变量g 为Group类型
调用Do函数。返回值赋值为v.
判断v是否为bar (string)
2.2 TestDoErr
定义一个变量g 为Group类型
增加一个自定义错误如下:
someErr := errors.New("Someerror")
然后调用Do函数。返回为自定义的错误。
然后判断返回的错误是否正确。
2.3 TestDoDupSuppress
定义一个变量g 为Group类型
一个返回函数,用与检测10循环也只调用了一次。
fn := func() (interface{}, error) {
atomic.AddInt32(&calls,1)
return<-c, nil
}
定义一个常数10.
循环10次
每次wg.Add(1),然后并发go func()
然后等待100ms让goroutings可以阻塞一会。
函数返回的是一个通道变量。
给通道变量赋值bar.
2.4 测试结果
测试如下
=== RUN TestDo
--- PASS: TestDo (0.00s)
=== RUN TestDoErr
--- PASS: TestDoErr (0.00s)
=== RUN TestDoDupSuppress
--- PASS: TestDoDupSuppress(0.10s)
PASS
ok test 0.227s
有疑问加站长微信联系(非本文作者)