减少下层服务的压力 SingleFlight
jimyag/singleflight 包主要是用来做并发控制,常见的比如防止缓存击穿 合并查询请求。
特性
- 实用,可以大幅度提升合并查询请求的效率
- 简单 所有的代码仅有 90 行,逻辑简单
- 支持泛型,基于
go1.18和go-zero实现的泛型SingleFlight
功能
防止缓存击穿
缓存击穿:缓存在某个时间点过期的时候,恰好在这个时间点对这个 Key 有大量的并发请求过来,这些请求发现缓存过期一般都会从后端 DB 加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把后端 DB 压垮。
通过 SingleFlight 可以将对同一个 Key 的并发请求进行合并,只让其中一个请求到数据库进行查询,其他请求共享同一个结果,可以很大程度提升并发能力。
查询缓存时,合并请求,提升服务性能
假设有一个 IP 查询的服务,每次用户请求先在缓存中查询一个 IP 的归属地,如果缓存中有结果则直接返回,不存在则进行 IP 解析操作。
n 个用户请求查询同一个 IP(8.8.8.8)就会对应 n 个 Redis 的查询,在高并发场景下,如果能将 n 个 Redis 查询合并成一个 Redis 查询,那么性能肯定会提升很多,而 SingleFlight 就是用来实现请求合并的。
快速入手
Install
|
|
Usage
启动 10 个携程同时对calls加 1,最终的结果只加了一次
|
|
|
|
如何实现
万老师的文章中写的很详细,这边直接引用万老师的分析。
先看代码结构:
|
|
然后看最核心的 Do方法做了什么事情:
|
|

从上图可知,其实关键就两步:
- 判断是第一个请求的协程(利用 map)
- 阻塞住其他所有协程(利用 sync.WaitGroup)
来看下 g.createCall(key) 如何实现的:
|
|
|
|