go-zero如何同一时刻相同请求合并

/core/syncx/singleflight.go

func (g *flightGroup) Do(key string, fn func() (any, error)) (any, error) {
	c, done := g.createCall(key)
	if done {
		// 此时,第一个协程执行完了,其他协程直接拿结果就行了。
		return c.val, c.err
	}
	// 第一个到达的协程,执行任务函数fn。
	g.makeCall(c, key, fn)
	return c.val, c.err
}

第一个协程到达,此时没有任务,新建call记录,其他协程就知道了已经有人再做了。

func (g *flightGroup) createCall(key string) (c *call, done bool) {
	g.lock.Lock()
	// 查看这个key是否已有其他协程在执行
	if c, ok := g.calls[key]; ok {
		g.lock.Unlock()
		// 等其他协程执行完
		c.wg.Wait()
		// 拿到其他协程执行完所得到的结果。(自己就不必亲自做一遍)
		return c, true
	}

	// 我是第一个协程A,算了,我执行亲自做一遍吧,然后把结果共享给其他同样请求的协程。
	c = new(call)
	c.wg.Add(1)
	// 其他协程调用,就知道我正在执行,您们就等着摘果实吧。
	g.calls[key] = c
	g.lock.Unlock()
	// 第一个协程,返回false,意思是现在就去执行一下任务。
	return c, false
}

第一个协程,执行任务函数fn(),返回结果 赋值于 c.val

func (g *flightGroup) makeCall(c *call, key string, fn func() (any, error)) {
	defer func() {
		g.lock.Lock()
		// 第一个协程执行完任务,就删除任务key了,其他同一时刻的相同请求协程,反正都查询key过了,且都在等待中。
		delete(g.calls, key)
		g.lock.Unlock()
		c.wg.Done()
	}()

	// 第一个协程,执行任务函数fn(),返回结果 赋值于 c.val
	c.val, c.err = fn()
}