golang的Fan模式在项目中实战,我后悔了

简介: golang的Fan模式在项目中实战,我后悔了

别在树下徘徊,别在雨中沉思,别在黑暗中落泪——梅里美《卡门》


1. 前言



不好意思,今天是中国传统节日七夕,我专门在家带娃,我媳妇去修理牙齿去了,日子特殊,需要做点有意义的事情,我就尝试着一边带娃一边敲文章,结果从早晨到下午才完稿,中间过程省略13个字


2. Fan in Fan out模式



先看图:


640.png

Fan out模式:

多个goroutine从同一个通道读取数据,直到该通道关闭。OUT是一种张开的模式,所以又被称为扇出,可以用来分发任务。多个goroutine有自己的chan,这样将结果暂存下来。


Fan in模式:

1个goroutine从多个通道读取数据,直到这些通道关闭。IN是一种收敛的模式,所以又被称为扇入,用来收集处理的结果。这样我们可以从最后这一个chan直接处理数据了。


3. 实战



package main
import (
 "fmt"
 "runtime"
 "sync"
 "time"
)
func main() {
 done := make(chan interface{})
 defer close(done)
 start := time.Now()
 //生成随机数切片
 genSlice := func() []int {
  sl := make([]int, 0)
  for i := 0; i< 1000000; i++ {
   sl = append(sl, i)
  }
  return sl
 }
// 把上面生成的slice转换为chan序列
 gen := func(done <- chan interface{}, nums ...int) <- chan int {
  intStream := make(chan int)
  go func() {
   defer close(intStream)
   for _, n := range nums {
    select {
    case <-done:
     return
    case intStream <- n:
    }
   }
  }()
  return intStream
 }
  // 对读取的数字进行相加操作
 sq := func (done <-chan interface{}, in <-chan int) <-chan int {
  out := make(chan int)
  go func() {
   defer close(out)
   for n := range in {
    select {
     case out <- n + n:
     case <-done:
      return
    }
   }
  }()
  return out
 }
// 获取cpu核数 多少核数运行多少goroutine 即Fan out模式
 nCpu := runtime.NumCPU()
 finders := make([]<-chan int, nCpu)
 in := gen(done, genSlice()...)
 for i:=0; i<nCpu; i++ {
  finders[i] = sq(done, in)
 }
 fmt.Println("cpu numbers is ", nCpu)
//Fan in模式
 fanIn := func(done chan interface{}, chans ...<-chan int) <- chan int{
  var wg = sync.WaitGroup{}
  multiPlexedStream := make(chan int)
  multiplex := func(c <- chan int) {
   defer wg.Done()
   for i:=range c {
    select {
    case <-done:
     return
    case multiPlexedStream <- i:
    }
   }
  }
  wg.Add(len(chans))
  for _, c := range chans {
   go multiplex(c)
  }
  go func() {
   wg.Wait()
   close(multiPlexedStream)
  }()
  return multiPlexedStream
 }
  // 
 for i:=range fanIn(done, finders...) {
  fmt.Println(i)
 }
 fmt.Println(time.Since(start))
}

我们看到就是nCpu的goroutine去调用sq执行操作,返回各自的chan,最后通过range操作将多个chan扇入到一个chan中。


4. 长话短说



我通过性能分析工具试了下运行上面代码需要多久?答案是5s,但是我用普通模式耗时是3s,普通模式如下:


for i := range sq(done, gen(done, genSlice()...)) {
  fmt.Println(i)
 }
 fmt.Println(time.Since(start))

普通模式没有扇出,没有扇入,运行时间很快,为什么呢?我们当前程序的瓶颈在FAN-IN,sq函数很快就完成,multiplex函数它把1000000个数据写入到1个通道的时候出现了瓶颈,适当使用带缓冲通道可以提高程序性能,例如multiPlexedStream := make(chan int, 10000)


5. 优势



FAN模式能提高Golang并发的性能(利用CPU核数),如果想以后运用自如,用到自己的项目中去,还是要多写去尝试一下,因为有时候Fan模式不一定是最优的。


6. 参考



google官方:https://blog.golang.org/pipelines

sf:https://segmentfault.com/a/1190000017182416


7. 关注公众号



 微信公众号:堆栈future

相关文章
|
17小时前
|
前端开发 Go
Golang深入浅出之-Go语言中的异步编程与Future/Promise模式
【5月更文挑战第3天】Go语言通过goroutines和channels实现异步编程,虽无内置Future/Promise,但可借助其特性模拟。本文探讨了如何使用channel实现Future模式,提供了异步获取URL内容长度的示例,并警示了Channel泄漏、错误处理和并发控制等常见问题。为避免这些问题,建议显式关闭channel、使用context.Context、并发控制机制及有效传播错误。理解并应用这些技巧能提升Go语言异步编程的效率和健壮性。
7 5
Golang深入浅出之-Go语言中的异步编程与Future/Promise模式
|
2天前
|
设计模式 Go 调度
Golang深入浅出之-Go语言中的并发模式:Pipeline、Worker Pool等
【5月更文挑战第1天】Go语言并发模拟能力强大,Pipeline和Worker Pool是常用设计模式。Pipeline通过多阶段处理实现高效并行,常见问题包括数据竞争和死锁,可借助通道和`select`避免。Worker Pool控制并发数,防止资源消耗,需注意任务分配不均和goroutine泄露,使用缓冲通道和`sync.WaitGroup`解决。理解和实践这些模式是提升Go并发性能的关键。
16 2
|
2天前
|
JSON 监控 安全
Golang深入浅出之-Go语言中的反射(reflect):原理与实战应用
【5月更文挑战第1天】Go语言的反射允许运行时检查和修改结构,主要通过`reflect`包的`Type`和`Value`实现。然而,滥用反射可能导致代码复杂和性能下降。要安全使用,应注意避免过度使用,始终进行类型检查,并尊重封装。反射的应用包括动态接口实现、JSON序列化和元编程。理解反射原理并谨慎使用是关键,应尽量保持代码静态类型。
12 2
|
3天前
|
Go 开发者
Golang深入浅出之-Go语言项目构建工具:Makefile与go build
【4月更文挑战第27天】本文探讨了Go语言项目的构建方法,包括`go build`基本命令行工具和更灵活的`Makefile`自动化脚本。`go build`适合简单项目,能直接编译Go源码,但依赖管理可能混乱。通过设置`GOOS`和`GOARCH`可进行跨平台编译。`Makefile`适用于复杂构建流程,能定义多步骤任务,但编写较复杂。在选择构建方式时,应根据项目需求权衡,从`go build`起步,逐渐过渡到Makefile以实现更高效自动化。
14 2
|
4天前
|
JSON JavaScript 前端开发
Golang深入浅出之-Go语言JSON处理:编码与解码实战
【4月更文挑战第26天】本文探讨了Go语言中处理JSON的常见问题及解决策略。通过`json.Marshal`和`json.Unmarshal`进行编码和解码,同时指出结构体标签、时间处理、omitempty使用及数组/切片区别等易错点。建议正确使用结构体标签,自定义处理`time.Time`,明智选择omitempty,并理解数组与切片差异。文中提供基础示例及时间类型处理的实战代码,帮助读者掌握JSON操作。
17 1
Golang深入浅出之-Go语言JSON处理:编码与解码实战
|
10天前
|
存储 测试技术 Go
Golang框架实战-KisFlow流式计算框架(2)-项目构建/基础模块-(上)
KisFlow项目源码位于&lt;https://github.com/aceld/kis-flow,初始阶段涉及项目构建和基础模块定义。首先在GitHub创建仓库,克隆到本地。项目目录包括`common/`, `example/`, `function/`, `conn/`, `config/`, `flow/`, 和 `kis/`。`go.mod`用于包管理,`KisLogger`接口定义了日志功能,提供不同级别的日志方法。默认日志对象`kisDefaultLogger`打印到标准输出。
419 1
Golang框架实战-KisFlow流式计算框架(2)-项目构建/基础模块-(上)
|
15天前
|
存储 监控 Go
Golang框架实战-KisFlow流式计算框架(1)-概述
KisFlow是针对缺乏数仓平台但又有实时计算需求的企业的解决方案,它提供分布式批量消费、有状态流式计算、数据流监控和分布式任务调度等功能。通过KisFunction实现业务逻辑复用,减轻对业务数据库的压力。系统包括流式计算层和任务调度层,支持多种数据源和中间件集成。KisConfig用于配置管理,KisFunction是基本计算单元。设计目标是使业务工程师能轻松进行流式计算。项目源码可在GitHub查看:https://github.com/aceld/kis-flow。
45 0
Golang框架实战-KisFlow流式计算框架(1)-概述
|
2月前
|
Go
第十二章 Golang家庭收支记账软件项目
第十二章 Golang家庭收支记账软件项目
40 2
|
3月前
|
监控 数据可视化 数据挖掘
Golang性能分析神器:pprof与火焰图实战揭秘
在软件开发的世界里,性能分析如同一把锋利的剑,它能帮助开发者洞悉程序的运行状态,发现并解决那些隐藏在代码深处的性能瓶颈。而在Go语言的生态系统中,pprof无疑是这把剑中的佼佼者。本文将带你深入了解pprof的使用方法,并通过火焰图这一直观的工具,让你对性能分析有一个全新的认识。
|
3月前
|
缓存 Cloud Native 测试技术
Golang 乐观锁实战:Gorm 乐观锁的优雅使用
在现代软件开发中,数据一致性是一个永恒的话题。随着系统规模的扩大和并发操作的增加,如何有效地处理并发冲突,确保数据的完整性,成为了开发者必须面对的挑战。本文将带你深入了解 Golang 中 Gorm ORM 库的乐观锁机制,并通过实际示例,展示如何在项目中优雅地使用乐观锁。