golang workerpool 源碼閱讀

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

今天讀了一下 fasthttp 的源碼,其中讀到了 workpool ,做了一些注釋。

package fasthttpimport (    "net"    "runtime"    "strings"    "sync"    "time")// workerPool serves incoming connections via a pool of workers// in FILO order, i.e. the most recently stopped worker will serve the next// incoming connection.//// Such a scheme keeps CPU caches hot (in theory).type workerPool struct {    // Function for serving server connections.    // It must leave c unclosed.    WorkerFunc func(c net.Conn) error //註冊的conn 處理函數    MaxWorkersCount int //最大的工作協程數    LogAllErrors bool    MaxIdleWorkerDuration time.Duration //協程最大的空閑時間,超過了就清理掉,其實就是退出協程函數 ,退出 go    Logger Logger    lock         sync.Mutex    workersCount int  //當前的工作協程數    mustStop     bool //workpool 停止標記    ready []*workerChan //準備工作的協程,記當時還在閒置協程    stopCh chan struct{} //workpool 停止訊號    workerChanPool sync.Pool //避免每次頻繁分配workerChan,使用pool}type workerChan struct { //工作協程    lastUseTime time.Time    ch          chan net.Conn //阻塞chan 處理完了一個conn 通過for range 再處理下一個,都在一個協程裡面}func (wp *workerPool) Start() {    if wp.stopCh != nil {        panic("BUG: workerPool already started")    }    wp.stopCh = make(chan struct{})    stopCh := wp.stopCh    go func() {        var scratch []*workerChan        for {            wp.clean(&scratch) //定時清理掉協程  (workerChan)            select {            case <-stopCh:                return            default:                time.Sleep(wp.getMaxIdleWorkerDuration())            }        }    }()}func (wp *workerPool) Stop() {    if wp.stopCh == nil {        panic("BUG: workerPool wasn't started")    }    close(wp.stopCh) //停止    wp.stopCh = nil    // Stop all the workers waiting for incoming connections.    // Do not wait for busy workers - they will stop after    // serving the connection and noticing wp.mustStop = true.    wp.lock.Lock()    ready := wp.ready    for i, ch := range ready { //清空        ch.ch <- nil        ready[i] = nil    }    wp.ready = ready[:0]    wp.mustStop = true    wp.lock.Unlock()}func (wp *workerPool) getMaxIdleWorkerDuration() time.Duration {    if wp.MaxIdleWorkerDuration <= 0 {        return 10 * time.Second    }    return wp.MaxIdleWorkerDuration}func (wp *workerPool) clean(scratch *[]*workerChan) {    // 傳入scratch ,要淘汰的ch, 避免每次分配    maxIdleWorkerDuration := wp.getMaxIdleWorkerDuration()    // Clean least recently used workers if they didn't serve connections    // for more than maxIdleWorkerDuration.    currentTime := time.Now()    wp.lock.Lock()    ready := wp.ready    n := len(ready)    i := 0    for i < n && currentTime.Sub(ready[i].lastUseTime) > maxIdleWorkerDuration {        i++ //到期的ch 個數    }    *scratch = append((*scratch)[:0], ready[:i]...) //淘汰的ch,放到scratch    if i > 0 {        m := copy(ready, ready[i:]) //把需要保留的ch,平移到前面,並且幾下要保留的數量 m        for i = m; i < n; i++ {            ready[i] = nil //把ready 後面的ch淘汰 賦值nil        }        wp.ready = ready[:m] //保留的ch到ready    }    wp.lock.Unlock()    // Notify obsolete workers to stop.    // This notification must be outside the wp.lock, since ch.ch    // may be blocking and may consume a lot of time if many workers    // are located on non-local CPUs.    tmp := *scratch    for i, ch := range tmp { //淘汰的ch 賦值nil        ch.ch <- nil        tmp[i] = nil    }}func (wp *workerPool) Serve(c net.Conn) bool {    ch := wp.getCh() //擷取一個協程    if ch == nil {        return false    }    ch.ch <- c //傳入 conn 到協程    return true}var workerChanCap = func() int {    // Use blocking workerChan if GOMAXPROCS=1.    // This immediately switches Serve to WorkerFunc, which results    // in higher performance (under go1.5 at least).    if runtime.GOMAXPROCS(0) == 1 {        return 0    }    // Use non-blocking workerChan if GOMAXPROCS>1,    // since otherwise the Serve caller (Acceptor) may lag accepting    // new connections if WorkerFunc is CPU-bound.    return 1}()func (wp *workerPool) getCh() *workerChan {    var ch *workerChan //ch 是一個conn chan 阻塞的,通過for range 不停的處理不同的conn,可以看做是一個協程,不停的處理不同的連結    createWorker := false    wp.lock.Lock()    ready := wp.ready    n := len(ready) - 1    if n < 0 {        if wp.workersCount < wp.MaxWorkersCount {            createWorker = true            wp.workersCount++ //沒有可用的了需要 new        }    } else {        ch = ready[n]        ready[n] = nil        wp.ready = ready[:n] //擷取ch 並且ready - 1    }    wp.lock.Unlock()    if ch == nil {        if !createWorker {            return nil        }        vch := wp.workerChanPool.Get() //new 一個,這裡的new 其實是在 pool 拿一個workerChan,從這裡可以看出基本上只要是頻繁要分配的變數,都使用pool        if vch == nil {            vch = &workerChan{                ch: make(chan net.Conn, workerChanCap),            }        }        ch = vch.(*workerChan)        go func() { //建立一個協程處理            wp.workerFunc(ch)            wp.workerChanPool.Put(vch) //歸還 workerChan        }()    }    return ch}func (wp *workerPool) release(ch *workerChan) bool {    ch.lastUseTime = CoarseTimeNow() //更新最後使用這個協程的時間    wp.lock.Lock()    if wp.mustStop {        wp.lock.Unlock()        return false //如果停止了,則上層 停止協程    }    wp.ready = append(wp.ready, ch) //歸還 ch 到ready    wp.lock.Unlock()    return true}func (wp *workerPool) workerFunc(ch *workerChan) {    var c net.Conn    var err error    for c = range ch.ch { //不停的擷取 ch(阻塞的chan) ,處理不同的conn,在一個協程裡面        if c == nil {            break        }        if err = wp.WorkerFunc(c); err != nil && err != errHijacked { //註冊的conn連結處理函數            errStr := err.Error()            if wp.LogAllErrors || !(strings.Contains(errStr, "broken pipe") ||                strings.Contains(errStr, "reset by peer") ||                strings.Contains(errStr, "i/o timeout")) {                wp.Logger.Printf("error when serving connection %q<->%q: %s", c.LocalAddr(), c.RemoteAddr(), err)            }        }        if err != errHijacked {            c.Close()        }        c = nil //釋放conn        if !wp.release(ch) {            break        } //如果stop 了就 break    }    wp.lock.Lock()    wp.workersCount-- //釋放此協程    wp.lock.Unlock()}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.