多生产者单消费者: 一次即兴的go语言简单基础组件构思和研发

背景:

开发业务代码,需要获取企业所有部门内所有的人员。需要调用A接口(获取企业内所有部门id)先获取所有部门,然后遍历所有获取到部门id调用B接口(通过部门id获取该部门下的人员). 通过api调用后放入数据库构成 人员表.

问题:

  • 线上环境调用A接口后获取的部门列表有10w个部门,则需要调用10w次B接口. 获取时间巨慢.

解决想法:

  • 无疑便是萌发一次多生产者单消费者模型的公共组件的研发.

使用样例:

先定义一个生产者的interface, 实现生产货物的函数Produce:

type MultiProducerParams struct {
	//	Wg    *sync.WaitGroup //must be pointer or can not be transfer into here

	Begin int
	End   int

	//SouceData *[]map[string]string
}

func (mpp *MultiProducerParams) Produce() (interface{}, error) {

	time.Sleep(time.Millisecond * 1)

	return mpp.Begin, nil

}

定义一个消费货物的函数Consume:



var countNum = 0

func Consume(ps []Product) {
	time.Sleep(time.Millisecond * 1)
	/*
		fmt.Println("")
		for _, p := range ps {
			fmt.Printf("%+v,", p)
		}*/
	m.Lock()
	defer m.Unlock()

	countNum += len(ps)

}

进行使用:

关键函数:

wp := NewWorkPool(11, Produce,20) 创建工作池,第一个参数是工作池协程数,第二个是批量消费货物的函数,第三个是批量消费货物的数量
wp.Invoke(&mpp) 放入生产任务

使用代码:

var endNum = 1000
var m sync.Mutex
func Test_WorkPool(t *testing.T) 
{

	wp := NewWorkPool(11, Produce,20)

	delta := 2

	for i := 1; i <= endNum; i = i + 1 {
		begin := i * delta
		end := i*delta + delta

		var mpp MultiProducerParams

		mpp.Begin = begin
		mpp.End = end

		wp.Invoke(&mpp)

	}

	wp.Close()
	if countNum != endNum {
		t.Fatalf("not equal: countNum: %d, endNum: %d", countNum, endNum)
	}
}

结合实际:
在produce定义请求http操作,在consume定义入库操作,即可完成。

基础组件解析:

组成模块:

  • 协程复用和避免开启过多 (选择腾讯协程池"github.com/panjf2000/ants")
  • slice操作tips
  • 抽象架构设计,易用,通用

模块解析:
slice操作tips:

  • 预分配空间
  • 空间复用, cap空间保留.

预分配空间:

根据已知数组长度预先批量分配空间: ps = make([]Product, 0, bulkNum)

空间复用: 适当的由长数组截取成短数组能复用cap空间.(其实是一次阅读go-mysql-driver代码学习到的)

复用代码:

func BenchmarkWithArrayCap(b *testing.B) {

	bulkNum := 10

	ps := make([]Product, 0, bulkNum)

	nowNum := 0

	for i := 0; i < b.N; i++ {

		if nowNum >= bulkNum {
			ps = ps[0:0]
			nowNum = 0

			//b.Log(cap(ps), len(ps))
		}

		p := 1000
		ps = append(ps, p)
		nowNum = nowNum + 1

	}

}

无空间复用:

func BenchmarkWithArrayNoCap(b *testing.B) {

	bulkNum := 10

	ps := make([]Product, 0, bulkNum)

	nowNum := 0

	for i := 0; i < b.N; i++ {

		if nowNum >= bulkNum {
			ps = ps[bulkNum:bulkNum] // or ps=make([]Product,0,bulkNum)
			nowNum = 0

			//b.Log(cap(ps), len(ps))
		}

		p := 1000
		ps = append(ps, p)
		nowNum = nowNum + 1

	}

}

差异对比:

goos: linux
goarch: amd64
pkg: grutils/grpattern
BenchmarkWithArrayCap-8     	77996929	        13.6 ns/op	       8 B/op	       1 allocs/op
BenchmarkWithArrayNoCap-8   	32943752	        32.3 ns/op	      40 B/op	       1 allocs/op
PASS


附录: 实现代码:

https://github.com/gdgrc/grutils

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值