singleflight是go扩展库提供的一种并发原语,当有大量并发请求时,只允许一个请求去实际调用这个回调函数,等到这个请求返回结果的时候,再把结果返回给其他几个同时调用了相同函数的请求,这样可以减少并发调用的数量。在实际应用中,它能够在一个服务中减少对下游的并发重复请求。一个常见的使用场景是防止缓存击穿
所谓缓存击穿,指的是大量的并发请求同时查询一个缓存Key时,如果这个Key正好过期失效,就会导致大量的请求都打到数据库上,这种现象就叫作缓存击穿。而singleflight能够在有大量针对同一key的请求时,只让一个请求执行去获取数据,而其他协程阻塞等待结果的返回,因此能有效避免这种现象
1 | //核心数据结构如下: |
可以注意到,Do
封锁了其他并发协程调用fn
的可能,最终逻辑走向单独需要调用的fn
上,但我们需要给它做一些封装,这也就是doCall
方法
1 | func (g *Group) doCall(c *call, key string, fn func() (interface{}, error)) { |
当然还有一些其他的方法,如下:
1 | //与Do类似,不同之处在于Do是同步返回结果,DoChan返回一个channel(每个协程都有各自单独的channel),异步将结果返回 |
大致逻辑图如下: