参考:
一、背景
介绍:Go的singleflight库提供了一个重复的函数调用抑制机制。
场景:适用于并发读请求量较大的后台服务,以降低存储层的压力。
在大量请求同时请求某一个热点key的场景下,singleflight方法可以很好的解决缓存击穿问题。
- 优点:可以避免同一时间内,大量相同的流量请求都打到数据库上进而引起其压力。通过限制对同一个键值对的多次重复请求,减少对下游如MySQL的瞬时流量。
- 缺点:但是依然有可能阻塞大量请求导致系统等待获取缓存数据的goroutine激增,因此需要灵活使用
DoCall()
异步调用方法和Forget()
方法。
二、原理
将一组相同的请求合并成一个请求。实际上最终只会有 第一个 请求会访问DB,在其获取到结果后,通过本地内存对剩余其他阻塞的请求返回相同结果。
如上图所示:请求1、2、3同时请求相同的key,singleflight机制只会让请求1访问DB,请求1返回的value不仅返回给客户端1,也作为请求2、请求3的结果返回给客户端。这里的多请求理解为多个goroutine并发执行。
底层实际上是通过 go map(区分是否是相同key的请求) + lock(线程安全) + waitgroup(阻塞其他请求),将请求1从DB获取的结果直接通过本地内存去返回给其他相同的请求。
三、使用方法
// Do:传入key和fn回调函数,如果key相同,fn方法只会执行一次,同步等待
// 返回值v:表示fn执行结果
// 返回值err:表示fn的返回的err
// 返回值shared:表示是否是真实fn返回的还是从保存的map[key]返回的,也就是共享的
func (g *Group) Do(key string, fn func() (interface{
}, error)) (v interface{
}, err error, shared bool) {
...}
// DoChan:与Do方法类似,区别在于执行函数fn非阻塞,结果通过chan返回给同组请求
func (g *Group) DoChan(key string, fn