vitess源碼閱讀筆記cache系列之用go實現通用資源集區

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

更新至2012.4.8的vitess代碼

 

新的代碼增加了同步用的條件變數,沒有空閑資源時的排隊不再使用channel來同步(使用其它程式設計語言的同學可以方便的移植這個代碼了),

轉而使用condition variable。不再使用mu.RLock,統一使用Lock,不再糾結。 整體代碼清晰了許多。

為了進一步提高效能和代碼複用,vitess還提供了通用的池管理,RoundRobin.go中實現了通用的資源集區,方便管理池內資源總數,逾時。

先上第一道菜:RoundRobin在整個vitess架構中扮演的什麼角色?個人覺得RoundRobin的重要性在vitess中怎麼著也能算個丐幫的幾個長老之一吧,作為其它幾個pool的核心基礎如cache_pool.go就是RoundRobin + memcache client實現的,而conn_pool.go則是用RoundRobin實現的串連池。先看看RoundRobin.go的基本原理,老規矩,還是從資料結構入手// Factory is a function that can be used to create a resource.
type Factory func() (Resource, error)        //Factory 方法,資源建立函數,如connection

// Every resource needs to suport the Resource interface.  
type Resource interface {    
    Close()
    IsClosed() bool
}
這裡要注意的是使用RoundRobin資源集區來管理的對象必須實現Resouce介面 。

為什麼要實現這兩個介面呢?,因為資源集區需要知道如何Close逾時的資源,

以及哪些資源是閒置,哪些已經關閉了。

// RoundRobin allows you to use a pool of resources in a round robin fashion.
type RoundRobin struct {
    // mu controls resources & factory
    // Use Lock to modify, RLock otherwise
    mu        sync.RWMutex        
    resources chan fifoWrapper //用chan來類比一個FIFO的隊列,先來先服務
    factory   Factory

    // Use sync/atomic to access the following vars
    size        int64                //LQ: 池的總大小
    waitCount   int64                //LQ: 還有多少人在等池內出現空閑資源
    waitTime    int64                //LQ: 等待空閑資源總共花了多少時間
    idleTimeout int64                //LQ: 最多允許資源空閑多長時間,超過則close空閑資源
}

type fifoWrapper struct {
    resource Resource
    timeUsed time.timeUsed             //LQ: 用於控制逾時,初始值為資源上次進入池內的時間(見Put函數)

抱歉都快到電視劇的第N集了才看到golang裡面的重量級組件channel,簡單的理解可以認為channel是個

支援生產者消費者模型的同步隊列,而且是個不需要銷毀的隊列,golang會自動將不再使用的channel當記憶體回收掉。

其實池也是生產者消費者模型。所以需要外部提供生產資源的方法,也就是上面的Factory介面。這裡的可以簡單理解為c語言裡面的函數指標。RoundRobin的實現邏輯是這樣的,如果池內有資源,則可以Get成功,如果沒有,但還有容量,則用Factory建立一個資源。如果池內已經沒有空閑資源,則傻等,知道有空閑資源可用位置。為了瞭解資源集區的運作狀態,還記錄了等待的客戶有多少。總共等了多少時間,有了這些就可以方便的評估RoundRobin的效果。原始碼也是按照這個思路來寫的。

不多說,上代碼,詳細內容見標註的代碼注釋   

// Get will return the next available resource. If none is available, and capacity
// has not been reached, it will create a new one using the factory. Otherwise,
// it will indefinitely wait till the next resource becomes available.
func (self *RoundRobin) Get() (resource Resource, err error) {
        return self.get(true)
}

// TryGet will return the next available resource. If none is available, and capacity
// has not been reached, it will create a new one using the factory. Otherwise,
// it will return nil with no error.
func (self *RoundRobin) TryGet() (resource Resource, err error) {
        return self.get(false)
}

func (self *RoundRobin) get(wait bool) (resource Resource, err error) {
        self.mu.Lock()
        defer self.mu.Unlock()
        // Any waits in this loop will release the lock, and it will be
        // reacquired before the waits return.
        for {
                select {
                case fw := <-self.resources:
                        // Found a free resource in the channel
                        if self.idleTimeout > 0 && fw.timeUsed.Add(self.idleTimeout).Sub(time.Now()) < 0 {
                                // resource has been idle for too long. Discard & go for next.
                                go fw.resource.Close()
                                self.size--
                                continue
                        }
                        return fw.resource, nil
                default:
                        // resource channel is empty
                        if self.size >= int64(cap(self.resources)) {
                                // The pool is full
                                if wait {
                                        start := time.Now()
                                        self.available.Wait()//沒有空閑資源了,等著吧,不如上一版本的代碼自然啊
                                        self.recordWait(start)
                                        continue
                                }
                                return nil, nil
                        }
                        // Pool is not full. Create a resource.
                        if resource, err = self.waitForCreate(); err == nil {
                                // Creation successful. Account for this by incrementing size.
                                self.size++
                        }
                        return resource, err
                }
        }
        panic("unreachable")
}

func (self *RoundRobin) recordWait(start time.Time) {
        self.waitCount++
        self.waitTime += time.Now().Sub(start)
}

//LQ: 這裡的increment和decrement應該是多餘的,沒看明白作者是什麼目的,和驚群有啥關係
//為了避免self.factory()比較耗時,執行self.factory時unlock還是有必要的
func (self *RoundRobin) waitForCreate() (resource Resource, err error) {
        // Prevent thundering herd: increment size before creating resource, and decrement after.
        self.size++
        self.mu.Unlock()
        defer func() {
                self.mu.Lock()
                self.size--
        }()
        return self.factory()
}

  

在代碼注釋中可以看到,為了避免驚群效應,這裡採用的方式是先increment,本人也不太明白,為什麼這樣能避免驚群效應。

還請熟悉的朋友不吝賜教。

看完了Get發現排隊等待是那麼的自然,一行代碼的事情。再來看Put函數,我們會發現喚醒也是那麼的簡潔。

 // Put will return a resource to the pool. You MUST return every resource to the pool,


// even if it's closed. If a resource is closed, Put will discard it. Thread synchronization
// between Close() and IsClosed() is the caller's responsibility.
func (self *RoundRobin) Put(resource Resource) {
        self.mu.Lock()
        defer self.mu.Unlock()
        defer self.available.Signal()        //LQ: 排隊的兄弟該醒醒了

        if self.size > int64(cap(self.resources)) {
                go resource.Close()
                self.size--
        } else if resource.IsClosed() {
                self.size--
        } else {
                self.resources <- fifoWrapper{resource, time.Now()}
        }
}

  

接下來的系列將會分析其它依賴RoundRobin的幾個池的實現,然後分析vitess的各種功能是如何?的。休息,休息一會兒。

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.