使用singleflight,避免并发重复请求

2023年 8月 9日 13.9k 0

背景

高并发的场景下,经常会出现并发重复请求资源的情况。

比如说,缓存失效时,我们去请求db获取最新的数据,如果这个key是一个热key,那么在缓存失效的瞬间,可能会有大量的并发请求访问到db,导致db访问量陡增,甚至是打崩db,这种场景也就是我们常说的缓存击穿。

流程图-10.jpg

针对同一个key的并发请求,这些请求和响应实际上都是一样的。所以我们可以把这种并发请求优化为:只进行一次实际请求去访问资源,然后得到实际响应,所有的并发请求共享这个实际响应的结果

针对分布式场景,我们可以使用分布式锁来实现

针对单机场景,我们可以使用singleflight来实现

流程图-11.jpg

singleflight

singleflight是golang内置的一个包,这个包提供了对重复函数调用的抑制功能,也就是保证并发请求只会有一个实际请求去访问资源,所有并发请求共享实际响应。

使用

singleflight在golang sdk源码中的路径为:src/internal/singleflight

但是internal是golang sdk内部的包,所以我们不能直接去使用

使用步骤:

  • 引入go mod
  • 使用singleflight包
  • 引入go mod

    go get golang.org/x/sync
    

    使用singleflight包

    singleflight包主要提供了三个方法

    // 方法作用:保证并发请求只会执行一次函数,并共享实际响应
    // 请求参数
    // key:请求的唯一标识,相同的key会被视为并发请求
    // fn:实际需要执行的函数
    // 响应参数
    // v:实际执行函数的返回值
    // err:实际执行函数的错误
    // shared:返回值v是否被共享,若存在并发请求,则为true;若不存在并发请求则为false
    func (g *Group) Do(key string, fn func() (any, error)) (v any, err error, shared bool)

    // 方法作用:和Do类似,不过方法返回的是chan
    func (g *Group) DoChan(key string, fn func() (any, error)) (= 0 {
    stack = stack[line+1:]
    }
    return &panicError{value: v, stack: stack}
    }

    // call is an in-flight or completed singleflight.Do call
    type call struct {
    // 保证相同key,只会进行一次实际请求
    // 相同key的并发请求会共享返回
    wg sync.WaitGroup

    // These fields are written once before the WaitGroup is done
    // and are only read after the WaitGroup is done.
    // 实际执行函数的返回值和错误
    val interface{}
    err error

    // forgotten indicates whether Forget was called with this call's key
    // while the call was still in flight.
    // 是否已删除当前并发请求的key
    forgotten bool

    // These fields are read and written with the singleflight
    // mutex held before the WaitGroup is done, and are read but
    // not written after the WaitGroup is done.
    // 并发请求的次数
    dups int
    chans []chan 0
    }

    // DoChan is like Do but returns a channel that will receive the
    // results when they are ready.
    //
    // The returned channel will not be closed.
    func (g *Group) DoChan(key string, fn func() (interface{}, error))

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论