singleflight 使用场景
针对同一业务的同一批请求(需自定义缓存的 key),只放一个请求去执行,其他等待结果(和普通缓存还不一样), 可以在不使用缓存的情况下,保护下游业务;
这是一条测试修改
例如 1:在有缓存的数据读取场景中,缓存过期失效时且大并发场景中,瞬间会有大量请求压到数据库,当设置上缓存后才会恢复.但如果去数据库当中查询数据\内存中计算组装\设置缓存等操作耗时稍长,同样会存在很大的风险,瞬间的巨量数据库访问,可能会使数据库异常。
例如 1:同上,在无缓存的场景中, 如果一个业务完成处理需要 1s, 100 并发情况下, 这 1s 内都会被到服务器执行,会给服务器造成巨大的压力, 用 singleflight 只会有一个请求被真正处理, 其它的会等 1s(第一个请求处理完成),直接取第一个请求的处理结果 .
golang singleflight 用武之地,杨锡坤 2017-09-17如果每个请求都落到下游服务,通常会导致下游服务瞬时负载升高。如果使用缓存,如何判断当前接口请求的内容需要缓存下来?缓存的过期、更新问题?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error) { g.mu.Lock() if g.m == nil { g.m = make(map[string]*call) } if c, ok := g.m[key]; ok { g.mu.Unlock() c.wg.Wait() return c.val, c.err } c := new(call) c.wg.Add(1) g.m[key] = c g.mu.Unlock()
c.val, c.err = fn() c.wg.Done()
g.mu.Lock() delete(g.m, key) g.mu.Unlock()
return c.val, c.err }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| func TestDoDupSuppress(t *testing.T) { var g singleflight.Group var calls int32 fn := func() (interface{}, error) { fmt.Printf("inprocess %d\n", calls) atomic.AddInt32(&calls, 1) time.Sleep(time.Second * 1) return "ok", nil }
const n = 30 var wg sync.WaitGroup for i := 0; i < n; i++ { wg.Add(1)
go func(j int) { fmt.Printf("before request %d\n", j) v, err := g.Do("key", fn)
fmt.Printf("after request %d, %#v\n", j, v) if err != nil { fmt.Printf("Do error: %v\n", err) }
wg.Done() }(i) } wg.Wait() fmt.Printf("done calls= %d\n", calls)
}
|