摘要:對於一個大型網站來說,負載均衡是永恆的話題。隨著硬體技術的迅猛發展,越來越多的負載均衡硬體裝置湧現出來,如F5 BIG-IP、Citrix NetScaler、Radware等等,雖然可以解決問題,但其高昂的價格卻往往令人望而卻步,因此負載均衡軟體仍然是大部分公司的不二之選。nginx作為webserver的後起之秀,其優秀的反向代理功能和靈活的負載均衡策略受到了業界廣泛的關注。本文將以工業生產為背景,從設計實現和具體應用等方面詳細介紹nginx負載均衡策略。
關鍵字:nginx 負載均衡 反向代理
1.前言
隨著網際網路資訊的爆炸性增長,負載均衡(load balance)已經不再是一個很陌生的話題,顧名思義,負載均衡即是將負載分攤到不同的服務單元,既保證服務的可用性,又保證響應足夠快,給使用者很好的體驗。快速增長的訪問量和資料流量催生了各式各樣的負載均衡產品,很多專業的負載均衡硬體提供了很好的功能,但卻價格不菲,這使得負載均衡軟體大受歡迎,nginx就是其中的一個。
nginx第一個公開版本釋出於2004年,2011年釋出了1.0版本。它的特點是穩定性高、功能強大、資源消耗低,從其目前的市場佔有而言,nginx大有與apache搶市場的勢頭。其中不得不提到的一個特性就是其負載均衡功能,這也成了很多公司選擇它的主要原因。本文將從原始碼的角度介紹nginx的內建負載均衡策略和擴充套件負載均衡策略,以實際的工業生產為案例,對比各負載均衡策略,為nginx使用者提供參考。
2. 原始碼剖析
nginx的負載均衡策略可以劃分為兩大類:內建策略和擴充套件策略。內建策略包含加權輪詢和ip hash,在預設情況下這兩種策略會編譯進nginx核心,只需在nginx配置中指明引數即可。擴充套件策略有很多,如fair、通用hash、consistent hash等,預設不編譯進nginx核心。由於在nginx版本升級中負載均衡的程式碼沒有本質性的變化,因此下面將以nginx1.0.15穩定版為例,從原始碼角度分析各個策略。
2.1. 加權輪詢(weighted round robin)
輪詢的原理很簡單,首先我們介紹一下輪詢的基本流程。如下是處理一次請求的流程圖:
圖中有兩點需要注意,第一,如果可以把加權輪詢演算法分為先深搜尋和先廣搜尋,那麼nginx採用的是先深搜尋演算法,即將首先將請求都分給高權重的機器,直到該機器的權值降到了比其他機器低,才開始將請求分給下一個高權重的機器;第二,當所有後端機器都down掉時,nginx會立即將所有機器的標誌位清成初始狀態,以避免造成所有的機器都處在timeout的狀態,從而導致整個前端被夯住。
接下來看下原始碼。nginx原始碼的目錄結構很清晰,加權輪詢所在路徑為nginx-1.0.15/src/http/ngx_http_upstream_round_robin.[c|h],在原始碼的基礎上,針對重要的、不易理解的地方我加了註釋。首先看下ngx_http_upstream_round_robin.h中的重要宣告:
從變數命名中,我們就可以大致猜出其作用。其中,current_weight和weight的區別主要是前者為權重排序的值,隨著處理請求會動態的變化,後者是配置值,用於恢復初始狀態。
接下來看下輪詢的建立過程,程式碼如下圖所示。
這裡有個tried變數需要做些說明。tried中記錄了伺服器當前是否被嘗試連線過。他是一個點陣圖。如果伺服器數量小於32,則只需在一個int中即可記錄下所有伺服器狀態。如果伺服器數量大於32,則需在記憶體池中申請記憶體來儲存。對該點陣圖陣列的使用可參考如下程式碼:
最後是實際的策略程式碼,邏輯很簡單,程式碼實現也只有30行,直接上程式碼。
2.2. ip hash
ip hash是nginx內建的另一個負載均衡的策略,流程和輪詢很類似,只是其中的演算法和具體的策略有些變化,如下圖所示:
ip hash演算法的核心實現如下圖:
從程式碼中可以看出,hash值既與ip有關又與後端機器的數量有關。經過測試,上述演算法可以連續產生1045個互異的value,這是該演算法的硬限制。對此nginx使用了保護機制,當經過20次hash仍然找不到可用的機器時,演算法退化成輪詢。因此,從本質上說,ip hash演算法是一種變相的輪詢演算法,如果兩個ip的初始hash值恰好相同,那麼來自這兩個ip的請求將永遠落在同一臺伺服器上,這為均衡性埋下了很深的隱患。
2.3. fair
fair策略是擴充套件策略,預設不被編譯進nginx核心。其原理是根據後端伺服器的響應時間判斷負載情況,從中選出負載最輕的機器進行分流。這種策略具有很強的自適應性,但是實際的網路環境往往不是那麼簡單,因此要慎用。
2.4. 通用hash、一致性hash
這兩種也是擴充套件策略,在具體的實現上有些差別,通用hash比較簡單,可以以nginx內建的變數為key進行hash,一致性hash採用了nginx內建的一致性hash環,可以支援memcache。
3. 對比測試
本測試主要為了對比各個策略的均衡性、一致性、容災性等,從而分析出其中的差異性,並據此給出各自的適用場景。為了能夠全面、客觀的測試nginx的負載均衡策略,我們採用了兩個測試工具、在不同場景下做測試,以此來降低環境對測試結果造成的影響。首先簡單介紹測試工具、測試網路拓撲和基本的測試流程。
3.1. 測試工具
3.1.1 easyABC
easyABC是公司內部開發的效能測試工具,採用epool模型實現,簡單易上手,可以模擬GET/POST請求,極限情況下可以提供上萬的壓力,在公司內部得到了廣泛的使用。由於被測試物件為反向代理伺服器,因此需要在其後端搭建樁伺服器,這裡用nginx作為樁webserver,提供最基本的靜態檔案服務。
3.1.2 polygraph
polygraph是一款免費的效能測試工具,以對快取服務、代理、交換機等方面的測試見長。它有規範的配置語言PGL(Polygraph Language),為軟體提供了強大的靈活性。其工作原理如下圖所示:
polygraph提供client端和server端,將測試目標nginx放在二者之間,三者之間的網路互動均走http協議,只需配置ip+port即可。client端可以配置虛擬robot的個數以及每個robot發請求的速率,並向代理伺服器發起隨機的靜態檔案請求,server端將按照請求的url生成隨機大小的靜態檔案做響應。這也是選用這個測試軟體的一個主要原因:可以產生隨機的url作為nginx各種hash策略的key。
另外,polygraph還提供了日誌分析工具,功能比較豐富,感興趣的同學可以參考附錄中的相關材料。
3.2. 測試環境
本測試執行在5臺物理機上,其中被測物件單獨搭在一臺8核機器上,另外四臺4核機器分別搭建了easyABC、webserver樁和polygraph,如下圖所示:
3.3. 測試方案
首先介紹下關鍵的測試指標:
均衡性:是否能夠將請求均勻的傳送給後端
一致性:同一個key的請求,是否能落到同一臺機器
容災性:當部分後端機器掛掉時,是否能夠正常工作
以上述指標為指導,我們針對如下四個測試場景分別用easyABC和polygraph進行測試:
場景1 server_*均正常提供服務;
場景2 server_4掛掉,其他正常;
場景3 server_3、server_4掛掉,其他正常;
場景4 server_*均恢復正常服務。
上述四個場景將按照時間順序進行,每個場景將建立在上一個場景基礎上,被測試物件無需做任何操作,以最大程度模擬實際情況。另外,考慮到測試工具自身的特點,在easyabc上的測試壓力在17000左右,polygraph上的測試壓力在4000左右。以上測試均保證被測試物件可以正常工作,且無任何notice級別以上(alert/error/warn)的日誌出現,在每個場景中記錄下server_*的qps用於最後的策略分析。
3.4. 測試結果
表1和圖1是輪詢策略在兩種測試工具下的負載情況。對比在兩種測試工具下的測試結果會發現,結果完全一致,因此可以排除測試工具的影響。從圖表中可以看出,輪詢策略對於均衡性和容災性都可以做到很好的滿足。
表2和圖2是fair策略在兩種測試工具下的負載情況。fair策略受環境影響非常大,在排除了測試工具的干擾之後,結果仍然有非常大的抖動。從直觀上講,這完全不滿足均衡性。但是從另一個角度出發,恰恰是由於這種自適應性確保了在複雜的網路環境中能夠物盡所用。因此,在應用到工業生產中之前,需要在具體的環境中做好測試工作。
以下圖表是各種hash策略,所不同的僅僅是hash key或者是具體的演算法實現,因此一起做對比。實際測試中發現,通用hash和一致性hash均存在一個問題:當某臺後端的機器掛掉時,原有落到這臺機器上的流量會丟失,但是在ip hash中就不存在這樣的問題。正如上文中對ip hash原始碼的分析,當ip hash失效時,會退化為輪詢策略,因此不會有丟失流量的情況。從這個層面上說,ip hash也可以看成是輪詢的升級版。
圖5為ip hash策略,ip hash是nginx內建策略,可以看做是前兩種策略的特例:以來源ip為key。由於測試工具不便於模擬海量ip下的請求,因此這裡擷取線上實際的情況加以分析,如下圖所示:
圖5 ip hash策略
圖中前1/3使用輪詢策略,中間段使用ip hash策略,後1/3仍然是輪詢策略。可以明顯的看出,ip hash的均衡性存在著很大的問題。原因並不難分析,在實際的網路環境中,有大量的高校出口路由器ip、企業出口路由器ip等網路節點,這些節點帶來的流量往往是普通使用者的成百上千倍,而ip hash策略恰恰是按照ip來劃分流量,因此造成上述後果也就自然而然了。
4. 總結與展望
通過實際的對比測試,我們對nginx各個負載均衡策略進行了驗證。下面從均衡性、一致性、容災性以及適用場景等角度對比各種策略。(點選圖片檢視大圖)
以上從原始碼和實際的測試資料角度分析說明了nginx負載均衡的策略,並給出了各種策略適合的應用場景。通過本文的分析不難發現,無論哪種策略都不是萬金油,在具體的場景下應該選擇哪種策略一定程度上依賴於使用者對這些策略的熟悉程度。希望本文的分析和測試資料能夠對讀者有所幫助,更希望有越來越多、越來越好的負載均衡策略產出。