使用golang协程池控制并发请求

前言:

先聊下线程池,线程池存在是为了规避频繁的线程创建和销毁的开销,那么协程池呢? 我们知道在go的gmp体系下,协程的创建开销是很低的。几年前社区里不少开源项目里,为了规避协程的创建销毁的开销, 重复morestack扩充栈空间问题来实现协程池。比如风靡一时的fasthttp框架和tidb分布式数据库也引用了协程池。

但去年我在pingcap的tidb里看到了猫哥提的pr, 里面详细对比了 加协程池和直接 go func(){}的性能对比,直接 go newproc1的性能要比加协程池还高一些。想想也能理解,协程池也是有channel的开销的,channel里不仅有锁消耗,而且还有因阻塞造成的调度消耗。

该文章后续仍在不断的更新修改中, 请移步到原文地址 http://xiaorui.cc/?p=5866

我们用golang协程池的场景?

回到我们的主题,我们的场景主要是并发控制! 对的,用协程池来控制下游的并发。比如下游的某个api同一时间最多可以抗住200个长轮询请求。你可以在协程池里控制最多200个goroutine来处理。
当然你也可以使用信号量来控制,没有选用信号量的原因主要是不能动态的控制worker,没有缓冲队列的设计。当然,这是个伪命题。
这里不能用令牌和漏桶来限制并发,因为你不确定请求啥时候执行完,如令牌桶不好控制好令牌的生成速度。

go协程池的实现:

我自己实现了一个go协程池, 代码扔到github里了,有兴趣的可以看看。https://github.com/rfyiamcool/gpool , 求点赞。

实现的思路很简单,就是需要一个channel做缓冲池,然后预先开启几个goroutine消费该channel。通过回调的方法来实现异步和阻塞方法。

// xiaorui.cc

package main

import (
	"fmt"
	"sync"
	"time"

	"github.com/rfyiamcool/gpool"
)

var (
	wg = sync.WaitGroup{}
)

func main() {
	gp, err := gpool.NewGPool(&gpool.Options{
		MaxWorker:   5,                 // 最大的协程数
		MinWorker:   2,                 // 最小的协程数
		JobBuffer:   1,                 // 缓冲队列的大小
		IdleTimeout: 120 * time.Second, // 协程的空闲超时退出时间
	})

	if err != nil {
		panic(err.Error())
	}

	for index := 0; index < 1000; index++ {
		wg.Add(1)
		idx := index
		gp.ProcessAsync(func() {
			fmt.Println(idx, time.Now())
			time.Sleep(1 * time.Second)
			wg.Done()
		})
	}

	wg.Done()
}

总结:

原本是打算写一个golang hystrix熔断器的,看过java hystrix熔断器的实现,第一个步骤就是构建线程池。但写着写着,懒了,就先协程池吧。


对Golang感兴趣的朋友可以加群: 278517979 !!!
另外如果大家觉得文章对你有些作用! 如果想赏钱,可以用微信扫描下面的二维码, 感谢!
另外再次标注博客原地址  xiaorui.cc