广告
返回顶部
首页 > 资讯 > 后端开发 > GO >SingleFlight模式的Go并发编程学习
  • 585
分享到

SingleFlight模式的Go并发编程学习

2024-04-02 19:04:59 585人浏览 薄情痞子
摘要

目录应用场景应用方式源码解析总结项目地址最近接触到微服务框架Go-zero,翻看了整个框架代码,发现结构清晰、代码简洁,所以决定阅读源码学习下,本次阅读的源码位于core/syncx

最近接触到微服务框架Go-zero,翻看了整个框架代码,发现结构清晰、代码简洁,所以决定阅读源码学习下,本次阅读的源码位于core/syncx/singleflight.go

go-zeroSingleFlight的作用是:将并发请求合并成一个请求,以减少对下层服务的压力。

应用场景

  • 查询缓存时,合并请求,提升服务性能。 假设有一个 IP 查询的服务,每次用户请求先在缓存中查询一个 IP 的归属地,如果缓存中有结果则直接返回,不存在则进行 IP 解析操作。

如上图所示,n 个用户请求查询同一个 IP(8.8.8.8)就会对应 n 个 Redis 的查询,在高并发场景下,如果能将 n 个 Redis 查询合并成一个 Redis 查询,那么性能肯定会提升很多,而 SingleFlight就是用来实现请求合并的,效果如下:

  • 防止缓存击穿。

缓存击穿问题是指:在高并发的场景中,大量的请求同时查询一个 key ,如果这个 key 正好过期失效了,就会导致大量的请求都打到数据库,导致数据库的连接增多,负载上升。

通过SingleFlight可以将对同一个Key的并发请求进行合并,只让其中一个请求到数据库进行查询,其他请求共享同一个结果,可以很大程度提升并发能力。

应用方式

直接上代码:

func main() {
  round := 10
  var wg sync.WaitGroup
  barrier := syncx.NewSingleFlight()
  wg.Add(round)
  for i := 0; i < round; i++ {
    go func() {
      defer wg.Done()
      // 启用10个协程模拟获取缓存操作
      val, err := barrier.Do("get_rand_int", func() (interface{}, error) {
        time.Sleep(time.Second)
        return rand.Int(), nil
      })
      if err != nil {
        fmt.Println(err)
      } else {
        fmt.Println(val)
      }
    }()
  }
  wg.Wait()
}

以上代码,模拟 10 个协程请求 Redis 获取一个 key 的内容,代码很简单,就是执行Do()方法。其中,接收两个参数,第一个参数是获取资源的标识,可以是 redis 中缓存的 key,第二个参数就是一个匿名函数,封装好要做的业务逻辑。最终获得的结果如下:

5577006791947779410
5577006791947779410
5577006791947779410
5577006791947779410
5577006791947779410
5577006791947779410
5577006791947779410
5577006791947779410
5577006791947779410
5577006791947779410

从上看出,10个协程都获得了同一个结果,也就是只有一个协程真正执行了rand.Int()获取了随机数,其他的协程都共享了这个结果。

源码解析

先看代码结构:

type (
  // 定义接口,有2个方法 Do 和 DoEx,其实逻辑是一样的,DoEx 多了一个标识,主要看Do的逻辑就够了
  SingleFlight interface {
    Do(key string, fn func() (interface{}, error)) (interface{}, error)
    DoEx(key string, fn func() (interface{}, error)) (interface{}, bool, error)
  }
  // 定义 call 的结构
  call struct {
    wg  sync.WaitGroup // 用于实现通过1个 call,其他 call 阻塞
    val interface{}    // 表示 call 操作的返回结果
    err error          // 表示 call 操作发生的错误
  }
  // 总控结构,实现 SingleFlight 接口
  flightGroup struct {
    calls map[string]*call // 不同的 call 对应不同的 key
    lock  sync.Mutex       // 利用控制请求
  }
)

然后看最核心的Do方法做了什么事情:

func (g *flightGroup) Do(key string, fn func() (interface{}, error)) (interface{}, error) {
  c, done := g.createCall(key)
  if done {
    return c.val, c.err
  }
  g.makeCall(c, key, fn)
  return c.val, c.err
}

代码很简洁,利用g.createCall(key)对 key 发起 call 请求(其实就是做一件事情),如果此时已经有其他协程已经在发起 call 请求就阻塞住(done 为 true 的情况),等待拿到结果后直接返回。如果 done 是 false,说明当前协程是第一个发起 call 的协程,那么就执行g.makeCall(c, key, fn)真正地发起 call 请求(此后的其他协程就阻塞在了g.createCall(key))。 

从上图可知,其实关键就两步:

  • 判断是第一个请求的协程(利用map)
  • 阻塞住其他所有协程(利用 sync.WaitGroup)

来看下g.createCall(key)如何实现的:

func (g *flightGroup) createCall(key string) (c *call, done bool) {
  g.lock.Lock()
  if c, ok := g.calls[key]; ok {
    g.lock.Unlock()
    c.wg.Wait()
    return c, true
  }
  c = new(call)
  c.wg.Add(1)
  g.calls[key] = c
  g.lock.Unlock()
  return c, false
}

先看第一步:判断是第一个请求的协程(利用map)

g.lock.Lock()
if c, ok := g.calls[key]; ok {
  g.lock.Unlock()
  c.wg.Wait()
  return c, true
}

此处判断 map 中的 key 是否存在,如果已经存在,说明已经有其他协程在请求了,当前这个协程只需要等待,等待是利用了sync.WaitGroupWait()方法实现的,此处还是很巧妙的。要注意的是,map 在 Go 中是非并发安全的,所以需要加锁。

再看第二步:阻塞住其他所有协程(利用 sync.WaitGroup)

c = new(call)
c.wg.Add(1)
g.calls[key] = c

因为是第一个发起 call 的协程,所以需要 new 这个 call,然后将wg.Add(1),这样就对应了上面的wg.Wait(),阻塞剩下的协程。随后将 new 的 call 放入 map 中,注意此时只是完成了初始化,并没有真正去执行call请求,真正的处理逻辑在 g.makeCall(c, key, fn)中。

func (g *flightGroup) makeCall(c *call, key string, fn func() (interface{}, error)) {
  defer func() {
    g.lock.Lock()
    delete(g.calls, key)
    g.lock.Unlock()
    c.wg.Done()
  }()
  c.val, c.err = fn()
}

这个方法中做的事情很简单,就是执行了传递的匿名函数fn()(也就是真正call请求要做的事情)。最后处理收尾的事情(通过defer),也是分成两步:

  • 删除 map 中的 key,使得下次发起请求可以获取新的值。
  • 调用wg.Done(),让之前阻塞的协程全部获得结果并返回。

至此,SingleFlight 的核心代码就解析完毕了,虽然代码不长,但是这个思想还是很棒的,可以在实际工作中借鉴。

总结

  • map 非并发安全,记得加锁。
  • 巧用 sync.WaitGroup 去完成需要阻塞控制协程的应用场景。
  • 通过匿名函数 fn 去封装传递具体业务逻辑,在调用 fn 的上层函数中去完成统一的逻辑处理。

项目地址

https://GitHub.com/zeromicro/go-zero

以上就是SingleFlight模式的Go并发编程学习的详细内容,更多关于SingleFlight模式Go并发编程的资料请关注编程网其它相关文章!

您可能感兴趣的文档:

--结束END--

本文标题: SingleFlight模式的Go并发编程学习

本文链接: https://www.lsjlt.com/news/147414.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • SingleFlight模式的Go并发编程学习
    目录应用场景应用方式源码解析总结项目地址最近接触到微服务框架go-zero,翻看了整个框架代码,发现结构清晰、代码简洁,所以决定阅读源码学习下,本次阅读的源码位于core/syncx...
    99+
    2022-11-13
  • SingleFlight模式的Go并发编程实例分析
    这篇文章主要介绍了SingleFlight模式的Go并发编程实例分析的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇SingleFlight模式的Go并发编程实例分析文章都会有所收获,下面我们一起来看看吧。在go...
    99+
    2023-06-30
  • JUC 并发编程学习笔记(总)
    文章目录 1. 什么是JUC2. 进程和线程2.1 进程2.2 线程2.3 并发2.4 并行2.5 线程的状态2.6 wait 和 sleep 的区别 3. Lock锁(重点)3.1 传统Synchronized3.2 Lock...
    99+
    2023-08-18
    java JUC
  • 一起学习Go语言并发编程中的存储问题吧!
    在Go语言中,由于其天生的并发特性,存储问题是一个需要格外关注的问题。本文将介绍Go语言中并发编程中的存储问题,并提供一些示例代码以帮助读者更好地理解。 首先,我们需要了解Go语言中的并发模型。Go语言中的并发模型是基于goroutine...
    99+
    2023-10-17
    并发 教程 存储
  • Shell和Bash中的并发编程技巧:学习Go语言的经验
    Shell和Bash是常用的Linux命令行工具,用于执行各种系统任务和脚本。这些工具的强大之处在于它们能够支持并发编程,这意味着你可以同时执行多个任务,从而提高效率。本文将介绍如何在Shell和Bash中实现并发编程,并分享学习Go语言的...
    99+
    2023-09-30
    并发 shell bash
  • GO语言并发编程学习笔记:如何打包你的代码并实现并发执行?
    Go语言是一种开源的编程语言,由Google公司开发。它是一种高效的编程语言,特别适合开发并发程序。Go语言的并发编程能力是其最大的特点之一。在本文中,我们将介绍如何使用Go语言来打包代码并实现并发执行。 并发和并行 在讨论并发编程之...
    99+
    2023-07-01
    打包 并发 学习笔记
  • 并发编程学习笔记:PHP vs Django?
    随着互联网的不断发展,现代Web应用程序已经不再是简单的静态网站,而是具有高度交互性和实时响应性的动态应用程序。这种动态应用程序需要能够同时处理多个请求的并发编程技术。在本文中,我们将探讨两种常见的Web应用程序框架:PHP和Django...
    99+
    2023-09-15
    django 并发 学习笔记
  • Go语言开发快速学习CGO编程
    目录快速上手 CGO 程序基于 C 标准库实现最简单的 CGO 程序基于自己写的 C 函数构建 CGO 程序模块化以上例子用 Go 实现 C 函数并导出用 C 接口的方式实现 Go ...
    99+
    2023-05-14
    Go语言开发CGO编程 CGO编程
  • GO语言并发编程的学习笔记:如何打包你的代码?
    在GO语言中,如何打包你的代码是一个重要的话题。打包代码的目的是为了使其更易于管理和部署。打包代码的方式有很多种,本文将介绍一些常用的打包方式及其使用方法。 打包为可执行文件 GO语言可以将代码打包为可执行文件,这样你可以在没有安装G...
    99+
    2023-07-01
    打包 并发 学习笔记
  • Python 学习笔记:并发编程的必要性?
    Python 是一种高级编程语言,能够进行快速开发和编写,但是在处理大量数据或者高并发的情况下,Python 程序的执行效率会显著下降。这时候,我们需要使用并发编程来提高程序的性能和效率。 什么是并发编程? 并发编程是一种处理多个任务的方...
    99+
    2023-11-13
    学习笔记 关键字 并发
  • Java并发编程学习方法是什么
    这篇文章主要讲解了“Java并发编程学习方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java并发编程学习方法是什么”吧!一:并发基础和多线程首先需要学习的就是并发的基础知识,什么...
    99+
    2023-06-02
  • GO语言打包和并发编程学习笔记:如何高效地学习这两个主题?
    GO语言作为一门近年来崛起的编程语言,其优秀的并发处理机制和高效的包管理方式已经吸引了越来越多的开发者关注。因此,学习GO语言的打包和并发编程是非常必要的,也是GO语言学习中的重点难点。本文将分享一些学习这两个主题的经验和技巧,希望能够对...
    99+
    2023-07-01
    打包 并发 学习笔记
  • GO语言学习笔记:如何在打包过程中实现并发编程?
    GO语言是一种基于C语言的静态类型编程语言,它具有高并发、高效、易用等特点,适合用于开发网络应用、分布式系统、云计算等领域。在GO语言中,实现并发编程是其最大的特点之一。在本篇文章中,我们将学习如何在GO语言中实现并发编程。 GO语言中的...
    99+
    2023-07-01
    打包 并发 学习笔记
  • GO语言API并发编程学习笔记:如何写出高效的代码?
    随着计算机技术的不断发展,程序员们需要编写更加高效的代码来满足不断增长的计算需求。在这个过程中,GO语言成为了一个备受关注的编程语言,因为它在并发编程方面具有出色的表现。本文将介绍GO语言API并发编程的基本知识,并提供一些实用的技巧,帮...
    99+
    2023-06-13
    api 并发 学习笔记
  • 详解JUC并发编程中的进程与线程学习
    目录进程与线程进程线程同步异步串行并行执行时间创建和运行线程Thread 与 Runnable 的关系原理分析查看进程线程运行原理线程上下文切换start与run方法sleep方法s...
    99+
    2022-11-13
  • GO语言API并发编程难不难?来看看这位学习笔记!
    在当今互联网高速发展的时代,我们越来越需要高并发、高性能的应用来满足用户需求。而GO语言作为一门专门为高并发应用设计的语言,其API并发编程应用越来越受到开发者们的关注。那么,GO语言API并发编程难不难呢?本文将通过一个学习笔记的方式来...
    99+
    2023-06-13
    api 并发 学习笔记
  • 《Python 与 git:如何高效学习并发编程?》
    Python 与 git:如何高效学习并发编程? Python 是一种高级编程语言,具有简单易学、易用、高效的特点。它广泛应用于数据分析、人工智能等领域。而 git 是一种分布式版本控制系统,它可以协助团队协作开发,并且可以有效地管理代码的...
    99+
    2023-11-12
    git 并发 教程
  • Go Path学习笔记:如何在Linux中编写并运行Go程序?
    Go语言是一种在编写高性能网络服务时非常流行的编程语言,它具有快速编译、高效执行和强大的并发性等特点。如果你想在Linux环境下编写和运行Go程序,那么本篇文章将为你提供一些指导和技巧。 一、安装Go语言环境 在开始编写Go程序之前,你需...
    99+
    2023-07-05
    path 学习笔记 linux
  • 瞅一眼就能学会的GO并发编程使用教程
    目录GO的并发编程分享啥是并发编程呢为啥要有并发编程并发和并行的区别协程 goroutine 是啥GO 高并发的原因是啥GOLANG并发编程涉及哪些知识点呢Goroutine的那些事...
    99+
    2023-02-24
    GO并发编程使用 GO并发编程 GO并发
  • Python npm 学习笔记:并发编程技术详解?
    Python npm 学习笔记:并发编程技术详解 在现代编程中,无论是前端还是后端,我们都需要应对大量的并发请求。因此,掌握并发编程技术已经成为了每个程序员的必备技能之一。Python npm 提供了众多的并发编程技术,本文将对其中的几种常...
    99+
    2023-07-25
    npm 学习笔记 并发
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作