Golang中singleflight的实现原理
大家好,我是极客老墨。 上一篇我们学习了 singleflight 的用法,知道它可以抑制多个重复请求,极大地节约带宽、提升系统性能。用起来确实爽,但你有没有好奇过:这玩意儿底层到底是怎么实现的?为什么几行代码就能搞定并发控制? 今天我们就来扒一扒 singleflight 的源码,看看它的魔法到底藏在哪里。 核心思路:一个请求干活,其他请求白嫖 singleflight 的核心思路很简单:同一时间段内,对于相同的数据请求,只让第一个请求真正执行,其他请求全部阻塞等待。等第一个请求拿到结果后,直接把结果分享给所有等待的请求。 这就像食堂打饭,第一个人去窗口打饭,后面排队的人都等着。等第一个人打完,大家直接复制他的饭菜,不用再排队了。虽然这个比喻有点扯,但意思就是这么个意思。 回顾一下 singleflight 的公开 API: Group 对象:管理所有请求的大管家 Result 对象:执行结果的包装 Do 方法:同步执行,阻塞等待结果 DoChan 方法:异步执行,通过 channel 返回结果 从这些 API 可以推测:对于同一个 key,首个调用会执行真正的逻辑,后续相同 key 的调用都会阻塞,直到第一个请求返回。 singleflight 的源码不多,算上注释一共就 200 来行。我们来逐一分析。 Group:请求管理的大管家 先看 Group 的定义: 1type Group struct { 2 mu sync.Mutex // protects m 3 m map[string]*call // lazily initialized 4} Group 用一个 map[string]*call 存储所有正在执行的请求。为了保证并发安全,内部持有 sync.Mutex 锁来保护这个 map 的读写。 Group 有两个重要方法 Do 和 DoChan,在上一篇已经介绍过了。 再来回顾一下 Do 方法的定义: 1func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool) 参数说明: key:标记同一请求的 key,相同 key 认为是相同请求 fn:真正执行业务逻辑的方法 返回值: ...