singleflight源码阅读

Golang实现singleflight

代码地址: https://Github.com/gofish2020/easysingleflight

好处

提升系统的高可用性,避免突发的大流量导致整个系统的熔断。

singleflight源码阅读

代码分析

代码的实现就是利用锁+map这两个基本的数据结构

  • 锁:保证每次只放行一个请求
  • map:保证同一种类型请求(通过key来指定同类型),复用同一个处理结果
type (
Group struct {
calls map[string]*call // 懒初始化
mu sync.Mutex
}
)

call的作用就是存储结果

type (
call struct {
done chan struct{} // 处理完成的通知通道

val interface{} // 结果
err error // 是否出错
}
)

核心方法只有一个Do方法,代码流程图如下

singleflight源码阅读

代码的核心就是通过keyg.calls中查找是否存在相同的 *call对象;

  • 存在就使用该 *call对象,阻塞等待结果…
  • 不存在,就创建一个新的*call对象,将函数fn的执行结果保存到*call对象中,通过done解除阻塞
// bool值表示:本次返回值是缓存值,还是实际走的fn函数
func (g *Group) Do(key string, fn func() (interface{}, error)) (interface{}, error, bool) {

// 1.加锁
g.mu.Lock()
if g.calls == nil {
g.calls = make(map[string]*call)
}

// 2.key存在,说明有重复的调用,只能等待...
if c, ok := g.calls[key]; ok {
g.mu.Unlock()
<-c.done // 阻塞等待结果中..
return c.val, c.err, true
}

// 3.key不存在,说明这是第一个调用
cl := new(call)
cl.done = make(chan struct{})
g.calls[key] = cl
// 4. 在执行fn前解锁
g.mu.Unlock()

func() {
//defer 目的避免fn出现panic
defer func() {

if p := recover(); p != nil {
cl.err = newPanicError(p)
cl.val = nil
}

// 6. 删除key
g.mu.Lock()
delete(g.calls, key)
g.mu.Unlock()
// 7.通知阻塞在该call的协程,结束阻塞(呼应上面的阻塞)
close(cl.done)
}()
// 5.执行fn
cl.val, cl.err = fn()
}()

return cl.val, cl.err, false
}


原文始发于微信公众号(nullbody笔记):singleflight源码阅读

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/223600.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!