fasthttp的workerpool解析


fasthttp官方文档表示其性能达到golang标准库net/http的十倍,原因之一就是fasthttp使用了workerpool来处理conn,而标准库中是每次都新建一个goroutine。本文就来一窥fasthttp的workerpool的原理。

1. 结构

fasthttp中定义了workerpool结构体,其主要属性如下:

属性名类型用途
WorkerFuncServerHandler处理连接的函数
MaxWorkersCounttime.Durationworker最大的闲置时间,超过这个时间就会被清理
locksync.Mutex用于内部同步
workersCountint当前正在使用的worker数量
mustStopbool标记每个worker处理结束后退出
ready[]*workerChan保存可用的worker
stopChchan struct{}用于让后台清理goroutine退出
workerChanPoolsync.Pool保存被清理的worker,如果ready没有可用的worker,便通过此pool.Get()创建一个新的worker

值得注意的是:这里的worker和goroutine并不是对等的概念。worker其实就是一个conn的channel,每一个channel都有一个goroutine监听着。每当有连接到来时,就把连接塞到worker的channel中,这样监听的goroutine就可以执行对应的处理逻辑。

2. 原理

2.1 执行逻辑

workerpool 执行逻辑

2.2 启动worker

func (wp *workerPool) Serve(c net.Conn) bool {
   ch := wp.getCh()
   if ch == nil {
      return false
   }
   ch.ch <- c
   return true
}

当有连接需要处理时,先从workerpool中拿一个worker,再把conn塞到该worker的channel中去处理。至于如何获取worker,先判断workerpool的ready数组中是否有可用的worker,如果有则取一个出来处理conn,如果没有,则创建一个worker。

func (wp *workerPool) getCh() *workerChan {
   var ch *workerChan
   // ... 判断有没有可用的worker,若没有,是否可以创建新的worker

   if ch == nil {
      if !createWorker {
         return nil // 不能创建,直接返回空
      }
      vch := wp.workerChanPool.Get() // 通过sync.Pool创建一个新的workerChan
      ch = vch.(*workerChan)
      go func() {
         wp.workerFunc(ch)
         wp.workerChanPool.Put(vch) //放回pool供下次使用
      }()
   }
   return ch
}

那么每处理一个conn之后,怎样将worker挂起不销毁,供后面复用呢?继续看wp.workerFunc方法。

func (wp *workerPool) workerFunc(ch *workerChan) {
   var c net.Conn

   var err error
   for c = range ch.ch {
      if c == nil {
         break
      }

      if err = wp.WorkerFunc(c); err != nil && err != errHijacked {
      // ... 处理conn
        }

      if !wp.release(ch) {
         break
      }
   }

   wp.lock.Lock()
   wp.workersCount--
   wp.lock.Unlock()
}

处理完conn之后,会调用release函数将worker(其实就是对应的channel)保存在ready数组,相当于告知系统该worker可用,然后此goroutine在监听ch.ch时由于阻塞被系统挂起,但是并没有销毁,等待下次从ready数组中取出去处理conn。

2.3 停止worker

从workerFunc.workerFunc()中的for循环可以看出,如果要停止一个worker,只要给对应的ch塞nil,就会跳出for循环,从而goroutine执行结束。

2.4 后台清理worker

如果worker保存在ready数组中过久,那么说明要处理的连接并不多,而worker闲置过久也是一种资源浪费,所以fasthttp会对worker进行回收。
workerpool的ready成员保存了可用的worker,后台每隔一个MaxIdleWorkerDuration时间就遍历一次ready,将idle时间超过MaxIdleWorkerDuration的worker停止。

go func() {
   var scratch []*workerChan
   for {
      wp.clean(&scratch)
      select {
      case <-stopCh:
         return
      default:
         time.Sleep(wp.getMaxIdleWorkerDuration())
      }
   }
}()

每个worker记录了上次执行的时间(lastUseTime),ready中的worker按照lastUseTime顺序排列。所以通过二分法可以知道需要清理的有哪些worker,然后给这些worker的ch塞nil便可停止。

3. 总结

  1. sync.Pool是资源复用的一种重要手段。
  2. workerpool的思想值得借鉴。这里的worker其实就是通过goroutine +监听专有channel。当有对象从channel中取出并执行时,就是一个运行中的worker。当处理完,继续监听channel时,就是一个等待的worker。此时放到池子(ready)里,等待下一次取出执行。可以参考这篇文章
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jevenabc

请我喝杯咖啡吧~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值