内容简介:原文:阅读本编文章需要go语言基础和对资源池有一些了解。go 版本为1.11,
声明
阅读本编文章需要 go 语言基础和对资源池有一些了解。
go 版本为1.11, FastHTTP 为2018-11-23的最新master版本
前言
在开始前我们先来简单定义一下协程池: 能够达到协程资源复用
。在这个定义下协程池的实现可以说是“百花齐放”了,找一下热门的go语言开源项目都会有协程池的不同实现方式。 有基于链表实现的 Tidb
,有基于环形队列实现的 Jaeger
,有基于数组栈实现的 FastHTTP
等,种类繁多任君选择。这么多的协程池实现可以归纳成二种:
这2种实现中,个人比较喜欢第二种按需创建, FastHTTP 也是使用第二种方式,所以我们来看看它是如何实现的。
FastHTTP协程池简介
在介绍 FastHTTP 协程池之前先做一下简单的介绍。 workerChan 和协程一一对应,相同的生命周期,可以把 workerChan 看成是协程的门牌,使用凭证,引路子等。 整个协程池的实现主要由 workerPool 和 workerChan 组成。 FastHTTP 的协程池使用按需创建的方式,当有一个请求进来时创建一个协程,请求处理完成,就会把协程的 workerChan 放入 workerPool 的数组栈 [workerPool.ready ]里面,再有新的请求就从 workerPool.ready 获取 workerChan ,复用协程,以此循环。
协程池用在哪里
-
go官方原生
http.Server
net/http/server.go #2805
func (srv *Server) Serve(l net.Listener) error {
......
for {
rw, e := l.Accept()
......
//FastHTTP在这步使用协程池
go c.serve(ctx)
}
}
-
FastHTTP的
fasthttp.ListenAndServe
github.com/valyala/fasthttp/server.go 1489
func (s *Server) Serve(ln net.Listener) error {
......
for {
if c, err = acceptConn(s, ln, &lastPerIPErrorTime); err != nil {
......
}
//对应go原生的 go c.serve(ctx)
if !wp.Serve(c) {
......
}
......
}
}
在go原生的 http.Server
包中,当接收到新请求就会启动一个协程处理,而FastHTTP则使用协程池处理。
获取workerChan
github.com/valyala/fasthttp/workerpool.go #156
func (wp *workerPool) getCh() *workerChan {
var ch *workerChan
createWorker := false
wp.lock.Lock()
ready := wp.ready
n := len(ready) - 1
if n < 0 {
if wp.workersCount < wp.MaxWorkersCount {
createWorker = true
wp.workersCount++
}
} else {
//从尾部获取Ch
ch = ready[n]
ready[n] = nil
wp.ready = ready[:n]
}
wp.lock.Unlock()
if ch == nil {
//如果协程数超过上限,直接抛弃当前请求
if !createWorker {
return nil
}
vch := wp.workerChanPool.Get()
if vch == nil {
vch = &workerChan{
ch: make(chan chan struct{}, workerChanCap),
}
}
ch = vch.(*workerChan)
//ch和协程绑定
go func() {
wp.workerFunc(ch)
wp.workerChanPool.Put(vch)
}()
}
return ch
}
在go语言中不同协程之间的通讯使用 channel
,在协程池中也不例外,FastHTTP创建了一个协程,就会和一个 workerChan
绑定,使用方根据这个 workerChan
就可以使用协程池里的资源。从上面的代码可以看出,使用协程池的资源,都是先从Slice的尾部弹出 workerChan
,在把 workerChan
交给使用放,如果Slice没有 workerChan
就会创建。
把workerChan放入Slice尾部
github.com/valyala/fasthttp/workerpool.go #194
func (wp *workerPool) release(ch *workerChan) bool {
//用户清理
ch.lastUseTime = time.Now()
wp.lock.Lock()
if wp.mustStop {
wp.lock.Unlock()
return false
}
//往尾部追加
wp.ready = append(wp.ready, ch)
wp.lock.Unlock()
return true
}
当协程完成工作后,就会把 workerChan
放回Slice尾部,以待其他请求使用。
定期清理过期 workerChan
github.com/valyala/fasthttp/workerpool.go #98
func (wp *workerPool) clean(scratch *[]*workerChan) {
......
currentTime := time.Now()
wp.lock.Lock()
ready := wp.ready
n := len(ready)
i := 0
for i < n && currentTime.Sub(ready[i].lastUseTime) > maxIdleWorkerDuration {
i++
}
*scratch = append((*scratch)[:0], ready[:i]...)
if i > 0 {
m := copy(ready, ready[i:])
for i = m; i < n; i++ {
ready[i] = nil
}
wp.ready = ready[:m]
}
wp.lock.Unlock()
......
tmp := *scratch
for i, ch := range tmp {
//让协程停止工作
ch.ch <- nil
tmp[i] = nil
}
}
定期清理是为了避免在常态下空闲的协程过多,加重了调度层的负担。使用按需创建协程池的方式存在这样一个问题,高峰期的时候创建了很多协程,高峰期过后很多协程处于空闲状态,这就造成了不必要的开销。所以需要一种过期机制。在这里数组栈(FILO)的优点也体现出来了,因为栈的特点不活跃的 workerChan
都放在了数组的头部,所以只需要从数组头部开始轮询,一直到找到未过期的 workerChan
,再把这部分清理掉,就达到清理的效果,并且不需要轮询整个数组。
收益有多少
花了点时间对FastHTTP的协程池进行了压测 代码 。
apple:gopool apple$ go test -bench=. -test.benchmem goos: darwin goarch: amd64 pkg: study_go/gopool BenchmarkNotPool-4 10 4937881320 ns/op 107818560 B/op 401680 allocs/op BenchmarkFastHttpPool-4 10 380807481 ns/op 13444607 B/op 169946 allocs/op BenchmarkAntsPoll-4 10 429482715 ns/op 20756724 B/op 302093 allocs/op PASS ok study_go/gopool 72.891s
从上面的对比来看使用协程池的收益还不少。
结语
FastHTTP 协程池的实现方式是我所了解的几种实现中,性能是比较突出的,当然其他协程池的实现方式也很有学习参考价值,在这个过程中复习了链表,数组栈,环形队列的使用场景。收获颇多。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 物联网时代向传统商品条形码说再见RFID技术百花齐放
- 【源码阅读】AndPermission源码阅读
- ReactNative源码解析-初识源码
- 【源码阅读】Gson源码阅读
- Spring源码系列:BeanDefinition源码解析
- istio 源码 – Citadel 源码分析 (原创)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Google是如何控制世界的
(美)丹尼尔·伊克比亚 / 李军 / 东方出版社 / 2008-08 / 36.00元
秘Google的发展之路! Google,这个有着数百亿的网页存储量、每天两亿搜索次数的搜索引擎,最初仅仅是一个方程式。这个由拉里·佩奇和塞吉·布林两位天才创造出的超级算法甚至比可口可乐的配方还要保密。 当广告公司为自己网页在搜索结果中的排序争得头破血流时,Google正悠然地坐收渔翁之利,这种天才的拍卖广告链接的商业模式给Google带来了令人瞠目结舌的企业利润!仅仅从1999~20......一起来看看 《Google是如何控制世界的》 这本书的介绍吧!