原文:FastHTTP源码分析——“百花齐放”的协程池
声明
阅读本编文章需要go语言基础和对资源池有一些了解。
go 版本为1.11,FastHTTP为2018-11-23的最新master版本
前言
能够达到协程资源复用
- 提前创建协程:Jaeger,Istio,Tars等。
- 按需创建协程:Tidb,FastHTTP,Ants等。
这2种实现中,个人比较喜欢第二种按需创建,FastHTTP也是使用第二种方式,所以我们来看看它是如何实现的。
FastHTTP协程池简介
在介绍FastHTTP协程池之前先做一下简单的介绍。workerChan和协程一一对应,相同的生命周期,可以把workerChan看成是协程的门牌,使用凭证,引路子等。 整个协程池的实现主要由workerPool和workerChan组成。FastHTTP的协程池使用按需创建的方式,当有一个请求进来时创建一个协程,请求处理完成,就会把协程的workerChan放入workerPool的数组栈[workerPool.ready]里面,再有新的请求就从workerPool.ready获取workerChan,复用协程,以此循环。
协程池用在哪里
http.Servernet/http/server.go #2805
func (srv *Server) Serve(l net.Listener) error {
......
for {
rw, e := l.Accept()
......
//FastHTTP在这步使用协程池
go c.serve(ctx)
}
}
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) {
......
}
......
}
}
http.Server
获取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
}
channel workerChan workerChan workerChan workerChan 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
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
}
}
workerChanworkerChan
收益有多少
花了点时间对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协程池的实现方式是我所了解的几种实现中,性能是比较突出的,当然其他协程池的实现方式也很有学习参考价值,在这个过程中复习了链表,数组栈,环形队列的使用场景。收获颇多。
到此这篇关于“FastHTTP源码分析——“百花齐放”的协程池”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!
您可能感兴趣的文章:
整理的.NET高效开发的25款工具【值得收藏】
用于管理iptables的shell脚本一例
php实现简单用户登录功能程序代码
MySQL中group_concat函数使用例子
更改MySQL数据库名实例代码
专家教你如何有效的学习Drupal - Drupal问答
网页标题随机显示名言js代码
asp.net常用http状态码表
mysql导入导出数据时中文乱码的解决办法
PHP无限级分类菜单实例程序