Documentation
¶
Overview ¶
Package singleflight 提供并发请求合并(防击穿)工具。
fork 自 github.com/oekit/concurrency/singleflight(stdlib-only 泛型实现): pkg 是最底层模块(零 oekit 内部依赖),cache.SingleFlightCache 需要它 又不能反向依赖 concurrency,故内部化。concurrency 侧修 bug 时须同步本 fork。
同一个 key 的并发调用只执行一次 fn,其他调用共享结果。 适用于缓存穿透防护、token 刷新、配置加载等场景。
核心特性 ¶
- 基于 sync.Map + close(chan struct{}) 实现,零外部依赖
- 泛型支持,类型安全
- race detector 友好
- panic 安全:fn panic 不会导致等待者死锁
快速接入 ¶
sf := singleflight.New[string]()
val, err := sf.Do(ctx, "token:wechat_app", func(ctx context.Context) (string, error) {
return fetchAccessToken(ctx)
})
原理 ¶
调用 Do(key, fn) 时: 1. sync.Map.LoadOrStore 保证只有一个 goroutine 成为 caller 2. caller 执行 fn,完成后 close(ready) 广播通知 3. 其他 goroutine (waiter) 通过 <-ready 等待,读取共享结果 4. close 提供 happens-before 保证,字段读取无 data race
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Flight ¶
type Flight[T any] struct { // contains filtered or unexported fields }
Flight 对同一个 key 的并发调用只执行一次 fn。 零值可用,无需初始化。
func (*Flight[T]) Do ¶
func (f *Flight[T]) Do(ctx context.Context, key string, fn func(ctx context.Context) (T, error)) (T, error)
Do 执行 fn,同一个 key 的并发调用共享结果。
如果多个 goroutine 同时调用 Do 且 key 相同:
- 只有第一个 goroutine(caller)实际执行 fn
- 其他 goroutine(waiter)阻塞等待结果
- caller 完成后所有 waiter 同时获得返回值
panic 处理(与 golang.org/x/sync/singleflight 语义一致):
- 若 fn panic,panic 向首个 caller 自然传播(保留原始调用栈)
- waiter 被唤醒并拿到由 panic 转化的 error(而非零值+nil 的伪成功—— 调用方无法区分"合法零值"与"执行失败",会静默污染下游缓存/状态)
- map 中的 key 记录会被清理,不会因 panic 残留导致后续调用死锁
Click to show internal directories.
Click to hide internal directories.