Go語言實戰(zhàn)之實現(xiàn)均衡器功能
前言
當我們需要處理成千上萬的的用戶請求時,當一臺服務器可能無法滿足這千萬級別的請求時就可能需要擴容,增加服務器的數(shù)量來維持響應請求。服務器數(shù)量增多了,那流量如何均衡分配也是一個問題。這時就需要一個負載均衡,進行協(xié)調(diào)可用服務器之間的流量。
本文主要講述使用 Golang 實現(xiàn)一個簡單的流浪均衡器。
負載均衡
負載均衡器就是一個能夠?qū)φ埱罅髁客ㄟ^算法將請求轉(zhuǎn)發(fā)到相應的后端服務。其基本是在所有后端服務前置。一個用戶請求過來先經(jīng)過負載均衡器,然后由負載均衡器轉(zhuǎn)發(fā)請求到具體的后端服務上。所以其功能相對來說其實很簡單:轉(zhuǎn)發(fā)請求。其核心就是,均衡算法然后實現(xiàn)均衡轉(zhuǎn)發(fā)請求。常用的算法有以下幾種:
輪詢算法
輪詢法就是通過順序輪流轉(zhuǎn)發(fā)到后端服務器上。這種方式是最簡單的,只需要按照順序進行分配,不關心各服務負載情況,只需關心服務是否可用。如果檢查服務存活可用則直接轉(zhuǎn)發(fā)到當前服務。
所以這種方法比較適合用在多個服務器間硬件能力和處理能力都差不多的情況,然后對請求進行拆分均衡的轉(zhuǎn)發(fā)到各服務上。
簡單實現(xiàn)輪詢例子:
type roundRobin struct { targets []*url.URL index int } func (rr *roundRobin) nextTarget() *url.URL { target := rr.targets[rr.index] rr.index = (rr.index + 1) % len(rr.targets) return target } func (rr *roundRobin) ServeHTTP(w http.ResponseWriter, r *http.Request) { target := rr.nextTarget() proxy := httputil.NewSingleHostReverseProxy(target) proxy.ServeHTTP(w, r) }
定義了一個名為 roundRobin 的結(jié)構(gòu)體,并實現(xiàn)了一個名為 nextTarget 的方法,該方法返回下一個目標服務器的 URL。在 ServeHTTP 方法中,我們使用 nextTarget 方法獲取下一個目標服務器的 URL,并創(chuàng)建一個反向代理對象來將請求轉(zhuǎn)發(fā)到選定的服務器。
加權輪詢算法
加圈輪詢算法是基于輪詢算法的。當后端服務處理能力有差別的時候,比如可能A服務處理能力強于B服務2倍,那可以通過加權方式控制A服務處理請求多于B服務。
type serverWeight int const ( ServerA serverWeight = 6 ServerB serverWeight = 3 ServerC serverWeight = 1 )
比如以上配置表示,每有10個請求就會有6個會轉(zhuǎn)發(fā)到A服務器,3個轉(zhuǎn)發(fā)到B服務器,1個轉(zhuǎn)發(fā)到C服務器。
最少連接數(shù)算法
最小連接數(shù)是轉(zhuǎn)發(fā)依據(jù)始終是按照服務器當前連接數(shù)最小的那個進行轉(zhuǎn)發(fā)。這種方式可以更加有效的利用后端服務,合理的分流到各服務器。該算法不僅要檢查各服務是否有效,還需要記錄各服務已存在的連接數(shù)量。有點像連接池管理。
當然最少連接數(shù)算法,也可以對它們增加權重,即在比較連接數(shù)時同時考慮各服務的權重,被選中的服務器其連接數(shù)與權重的比要最小才能被選中。
詳細實現(xiàn)
定義結(jié)構(gòu)體
其他的算法,這里就不一一展開說明,想了解可以自己搜索。本文主要實踐使用最少連接算法的負責均衡器。所以一個均衡器的工作是查找連接數(shù)最少的服務,然后根據(jù)服務地址轉(zhuǎn)發(fā)出去。當然還需要知道當前可用服務是否存活。所以可以先定義一個記錄這些信息的結(jié)構(gòu)體,如服務請求地址,存活狀態(tài)、連接數(shù)等相關信息。
type Backend interface { GetURL() string GetConnections() int } type leastConn struct { targets []Backend mutex sync.Mutex } func (lc *leastConn) leastTarget() Backend { var target Backend minConns := math.MaxInt32 for _, t := range lc.targets { lc.mutex.Lock() conns := t.GetConnections() lc.mutex.Unlock() if conns < minConns { minConns = conns target = t } } return target } func (lc *leastConn) ServeHTTP(w http.ResponseWriter, r *http.Request) { target := lc.leastTarget() targetURL := target.GetURL() proxy := httputil.NewSingleHostReverseProxy(targetURL) proxy.ServeHTTP(w, r) }
為了避免出現(xiàn)競爭情況,通過增加鎖來控制。RWMutex 適合用于讀多寫少的場景。支持多個 goroutie 可以同時獲取讀鎖,但是寫時僅支持一個 goroutine 占有。reverseProxy 官網(wǎng)的解釋:是一種 Http Handler ,接收請求并發(fā)送到另一臺服務器中,把響應代理回客戶端。所以 ReverseProxy 的作用就是轉(zhuǎn)發(fā)原始的請求。
在上面的代碼中國,我們定義了一個名為 Backend 的接口,并實現(xiàn)了一個名為 leastConn 的結(jié)構(gòu)體。在 leastConn 結(jié)構(gòu)體中,我們使用 Backend 接口代替直接使用 URL 來表示服務器,這樣我們可以將任何實現(xiàn)了 Backend 接口的服務器添加到負載均衡器中。
在 leastTarget 方法中,我們使用 Backend 接口的方法來獲取每個服務器的當前連接數(shù),并選擇連接數(shù)最少的服務器來處理該請求。
以上就是Go語言實戰(zhàn)之實現(xiàn)均衡器功能的詳細內(nèi)容,更多關于Go均衡器的資料請關注腳本之家其它相關文章!
相關文章
Go語言中使用 buffered channel 實現(xiàn)線程安全的 pool
這篇文章主要介紹了Go語言中使用 buffered channel 實現(xiàn)線程安全的 pool,因為Go語言自帶的sync.Pool并不是很好用,所以自己實現(xiàn)了一線程安全的 pool,需要的朋友可以參考下2014-10-10