10.go开源groupcache项目笔记——singleFlight

来源:互联网 发布:黑死馆杀人事件知乎 编辑:程序博客网 时间:2024/06/08 00:40

10.go开源groupcache项目笔记——singleFlight

1      singleFlight

这个包主要实现了一个可合并操作的接口

1.1     先定义一个回调函数结构call

type call struct {
    //类似javaCountdownLatch
    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 {
        // 已经在处理了就释放groupwaitwg上,我们看到wg1是在某个时间段内第一次请求时加的
        // 并且在完成fn操作返回后会wg done,那时wait等待就会返回,直接返回第一次请求的结果
        g.mu.Unlock()
        c.wg.Wait()
        return c.val, c.err
    }
    // 如果没有在处理,则创建回调,wg1,把回调存到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