前言
在阿裡七層流量入口接入層(Application Gateway)場景下, Nginx 官方的Smooth Weighted Round-Robin( SWRR )負載均衡算法已經無法再完美施展它的技能。 Tengine 通過實作新的負載均衡算法Virtual Node Smooth Weighted Round-Robin(VNSWRR )不僅優雅的解決了 SWRR 算法的缺陷,而且QPS處理能力相對于 Nginx 官方的 SWRR 算法提升了60%左右。
問題
接入層 Tengine 通過自研的動态 upstream 子產品實作動态服務發現,即運作時動态感覺後端應用機器擴縮容、權重調整和健康檢查等資訊。同時該功能可以做很多事情,比如使用者可通過調整後端應用某台機器的權重進而達到線上真實引流壓測目的。然而,這些操作在 Nginx 原生 SWRR 算法下卻可能引起不可逆轉的血案。

• 在接入層(Application Gateway)場景下, Nginx 的負載均衡算法 SWRR 會導緻權重被調高機器的QPS瞬間暴漲,如上圖App2-host-A機器當權重調整為2時,某一時刻流量會集中轉發到該機器;
• Nginx 的 SWRR 算法的處理時間複雜度是O(N),在大規模後端場景下 Nginx 的處理能力将線性下降;
綜上所述,對接入層 Tengine 的負載均衡轉發政策的改造及性能優化已迫在眉睫。
原生 SWRR 算法分析
在介紹案列之前,我們先簡單介紹下 Nginx 的負載均衡算法 SWRR 轉發政策及特點:
SWRR 算法全稱是Smooth Weighted Round-Robin Balancing,顧名思義該算法相比于其它權重輪詢(WRR)算法多一個smooth(平滑)的特性。
下面我們就一個簡單的列子來描述下該算法:
假設有3台機器A、B、C權重分别為5、1、1,其中數組s代表機器清單、n代表機器數量,每個機器的cw初始化為0、ew初始化為機器權重、tw代表本輪選擇中所有機器的ew之和、best表示本輪被選中的機器。簡單的描述就是每次選擇機器清單中cw值最大的機器,被選中機器的cw将會減去tw,進而降低下次被選中的機會,簡單的僞代碼描述如下:
best = NULL;
tw = 0;
for(i = random() % n; i != i || falg; i = (i + 1) % n) {
flag = 0;
s[i].cw += s[i].ew;
tw += s[i].ew;
if (best == NULL || s[i].cw > best->cw) {
best = &s[i];
}
}
best->cw -= tw;
return best;
請求編号 選擇前的權重值 被選中的server 選擇後的權重值
0 {5,1,1} A {-2,1,1}
1 {3,2,2} A {-4,2,2}
2 {1,3,3} B {1,-4,3}
3 {6,-3,4} A {-1,-3,4}
4 {4,-2,5} C {4,-2,-2}
5 {9,-1,-1} A {2,-1,-1}
6 {7,0,0} A {0,0,0}
其 SWRR 算法選擇的順序為:{ A, A, B, A, C, A, A }
而普通WRR算法選擇的順序可能為:{ C, B, A, A, A, A, A }
SWRR 相比于普通的WRR算法特點:平滑、分散 。
調高權重引發的血案
從上面的描述來看, SWRR 算法似乎已經比較完美了,但是在某些場景下還是有一定的缺陷,下面我們就一個真實的案列來看看它都有哪些缺陷:
一天早上,流量排程的同學匆忙的跑到我的工位,當時看他神色是尤為的緊張,心想肯定是出啥問題了。果不其然:"為啥我把中心機房某台機器的權重從1調整為2的時候,接入層 Tengine 并不是按照這個權重比例轉發流量恩?",當時被調高機器QPS變化趨勢如下圖所示:
注:其中深藍色曲線表示權重被調高機器的QPS變化,淺綠色曲線表示該叢集單機的平均QPS。
當時看到這個流量趨勢變化圖的時候也是一臉茫然,不過好在有圖有資料,那就可以先分析一下這個圖的幾個特征數字;由于部分資料敏感,詳細資料分析就不在此處展開。直接描述其現象和原因:
被調高權重的機器當時被分發到的流量基本上是該應用機房總流量的1/2,一段時間後該機器的流量才恢複到預期的權重比例。其原因就是由于接入層 Tengine 對後端機器資訊的變化是動态感覺熱生效的,而 Nginx 官方的 SWRR 算法政策第一次會選擇目前機器清單中權重最大的機器轉發流量。進而進一步導緻已感覺到後端機器權重變化的接入層 Tengine 都會将第一個請求轉發到權重被調高的機器上。
大規模下性能驟降
如下是在upstream裡面配置2000個後端,在反向代理場景下壓測 Nginx 的函數熱點圖如下所示。其中ngx_http_upstream_get_peer函數CPU消耗占比高達39%,其原因是因為 SWRR 算法的選取機器的時間複雜度為O(N) (其中N代表後端機器數量),這就相當于每個請求都要執行接近2000次循環才能找到對應本次轉發的後端機器。
• 壓測環境
CPU型号: Intel(R) Xeon(R) CPU E5-2682 v4 @ 2.50GHz
壓測工具:./wrk -t25 -d5m -c500 '
http://ip/t2000'
Tengine 核心配置:配置2個worker程序,壓力源 --長連接配接--> Tengine / Nginx --短連接配接--> 後端
下面我們做個試驗,控制變量是 upstream 裡面配置的 server 數量,觀察不同場景下 Nginx 的 QPS 處理能力以及響應時間RT變化情況。從圖中可以發現當後端 upstream 裡面的 server 數量每增加500台則 Nginx 的 QPS 處理能力下降 10% 左右,響應RT增長 1ms 左右。
從上面的分析基本上已經确認是 SWRR 算法存在如上兩個缺陷,下面就開始解決;
改進的 VNSWRR 算法
雖然經典的WRR算法(如随機數方式實作)可以在時間複雜度上達到 O(1) ,而且也可以避免 SWRR 算法調高權重的選取缺陷。但是在某些場景下(如小流量)可能造成後端的流量不均等問題,尤其是在流量瞬間暴漲的場景下有太多不可确定性。于是就構思是否有一種算法即能擁有 SWRR 算法的平滑、分散特點,又能具備 O(1) 的時間複雜度。是以就有了Virtual Node Smooth Weighted Round-Robin( VNSWRR )算法。
此處拿個列子來說明此算法:3台機器A、B、C權重分别為1、2、3,N代表後端機器數 、TW代表後端機器權重總和。
算法關鍵點
o 虛拟節點初始化順序嚴格按照 SWRR 算法選取,保證初始化清單裡的機器能夠分布足夠散列;
o 虛拟節點運作時分批初始化,避免密集型計算集中。每批次虛拟節點使用完後再進行下一批次虛拟節點清單初始化,每次隻初始化min(n, max)個虛拟節點;
算法描述
o Tengine 程式啟動或者運作時感覺後端機器資訊變化時,則建構TW個虛拟節點且第一次隻初始化N個節點(注:TW代表後端機器權重之和,N代表後端機器數);
o 各個程序設定随機起點輪詢位置,如上圖的Step 1對應的清單其起點位置指向B;
o 當請求到達後從設定的随機起點B位置輪詢虛拟節點清單,若輪詢到已經初始化的虛拟節點數組的末尾(如上圖的Step2紅色箭頭指向的位置),則初始化第二批虛拟節點(如上圖Step2對應的紅色節點),當所有虛拟節點初始化完後将不再做初始化工作(如上圖的Step3對應的狀态);
此方案不僅将算法時間複雜度從 O(N) 優化到 O(1) ,而且也避免了權重調高場景下帶來的問題。如下圖所示後端某台機器權重從1調整為2後,其QPS平滑的增長到預期比列。
算法效果比較
在同等壓測環境下(wrk壓測工具、500并發、長連接配接場景、upstream配置2000個server), Nginx 官方的 SWRR 算法CPU消耗占比高達39%(ngx_http_upstream_get_peer函數)。而 VNSWRR 算法在同等條件下CPU消耗占比隻有0.27%左右(ngx_http_upstream_get_ VNSWRR 函數),顯而易見 SWRR CPU消耗要高出一個數量級。
在上述壓測環境下, Nginx 官方的 SWRR 和改進的 VNSWRR 算法下的QPS處理能力如下圖所示:其中 VNSWRR 的QPS處理能力相對于 SWRR 算法提升60%左右。
下面我們來做個試驗,在 upstream 裡配置 server 數量變化的場景下,對比 VNSWRR 和 SWRR 算法觀察 Nginx 的 QPS 處理能力以及響應時間RT變化。
從圖中可以發現在 SWRR 算法下當 upstream 裡面的 server 數量每增加500台,則 Nginx 的 QPS 處理能力下降10%左右、響應RT增長1ms左右,而在 VNSWRR 算法下 Tengine 的 QPS 處理能力及RT基本上變化不大。
總結
正是這種大流量場景下才暴露出 Nginx 的一些問題,所謂業務與技術相輔相成,業務可促使新的技術誕生、新的技術賦能創造新的業務。 VNSWRR 算法即擁有 SWRR 算法的平滑、分散特點,也避免了其缺陷。同時在新算法下時間複雜度也從 O(N) 調整為 O(1) ,在大規模場景下 VNSWRR 的QPS處理能力相對于 Nginx 官方的 SWRR 算法提升60%左右。
本文作者:王發康(花名:毅松),GitHub ID @wangfakang ,Tengine 開源項目 maintainer,阿裡巴巴技術專家,負責阿裡巴巴 WEB 統一接入層的開發及維護。