劉佳祎,崔建明,智 春
(桂林理工大學(xué) a.現(xiàn)代教育技術(shù)中心;b.繼續(xù)教育學(xué)院;c.信息科學(xué)與工程學(xué)院, 廣西 桂林 541006)
因特網(wǎng)的快速發(fā)展、人們的需求越來(lái)越多樣化,促使網(wǎng)絡(luò)技術(shù)不斷升級(jí)和優(yōu)化,進(jìn)而導(dǎo)致服務(wù)器的并發(fā)訪問(wèn)量和負(fù)載量呈指數(shù)級(jí)增加。由于服務(wù)器物理內(nèi)存和CPU處理速度的限制,單一服務(wù)器的處理能力和負(fù)載能力無(wú)法滿足日益增長(zhǎng)的網(wǎng)絡(luò)需求。服務(wù)器節(jié)點(diǎn)一旦發(fā)生故障,就無(wú)法保證為用戶正常地提供服務(wù),甚至整個(gè)系統(tǒng)都有可能癱瘓,使得系統(tǒng)的可靠性和穩(wěn)定性無(wú)法得到保障。為避免發(fā)生上述現(xiàn)象,提出了服務(wù)器集群技術(shù),多臺(tái)相互獨(dú)立的服務(wù)器組合了一個(gè)龐大的服務(wù)器集群,可解決大量并發(fā)請(qǐng)求所造成的負(fù)載過(guò)大、請(qǐng)求錯(cuò)誤率高、響應(yīng)速度慢等問(wèn)題,實(shí)現(xiàn)系統(tǒng)的靈活性,進(jìn)而滿足網(wǎng)絡(luò)需求[1]。
負(fù)載均衡技術(shù)通過(guò)運(yùn)用相關(guān)算法將來(lái)自網(wǎng)絡(luò)中的高并發(fā)請(qǐng)求合理地分配給服務(wù)器集群中的節(jié)點(diǎn),目前經(jīng)典的算法有輪詢(RR)、 加權(quán)輪詢(WRR)、 最小連接(LC)、 加權(quán)最小連接(WCL)等[2-5]。隨著負(fù)載均衡技術(shù)的廣泛應(yīng)用,國(guó)內(nèi)外眾多學(xué)者對(duì)其進(jìn)行改進(jìn)并提出了一些新的算法。Xu等[6]針對(duì)原有的輪詢算法提出一種解決負(fù)載均衡問(wèn)題的新輪詢優(yōu)化算法,該算法在負(fù)載范圍和負(fù)載方差方面有很大改進(jìn)。文獻(xiàn)[7]中提出一種對(duì)負(fù)載信息評(píng)價(jià)的改進(jìn)策略,來(lái)增強(qiáng)算法的局部搜索能力。為充分利用服務(wù)器的資源、縮小平均響應(yīng)時(shí)間,孫喬等[8]提出了一種基于軟件定義網(wǎng)絡(luò)的分布式數(shù)據(jù)庫(kù)負(fù)載均衡算法。
針對(duì)多處理器來(lái)說(shuō),如何提高其性能、降低負(fù)載不均衡率成為學(xué)者們研究的重要問(wèn)題。文獻(xiàn)[9]利用動(dòng)態(tài)分配的任務(wù)中的線程數(shù)量來(lái)計(jì)算配置比率,從而為每個(gè)集群生成新的處理元數(shù)量。Singh等[10]提出的新方法優(yōu)點(diǎn)在于既保留了多路徑的優(yōu)點(diǎn),又盡可能地保持其開銷接近單路徑路由。針對(duì)如何在原有加權(quán)輪詢策略上更好地提高集群性能這一問(wèn)題, 文獻(xiàn)[11]提出一種基于服務(wù)器性能指標(biāo)的能夠動(dòng)態(tài)調(diào)整權(quán)重的負(fù)載均衡策略, 該方案使每個(gè)節(jié)點(diǎn)都可以分配到適合其負(fù)載能力的任務(wù),進(jìn)而提高集群的性能。
綜上所述,負(fù)載均衡技術(shù)占有重要地位,應(yīng)用前景廣闊。本文針對(duì)Nginx服務(wù)器內(nèi)置負(fù)載均衡策略的不足,進(jìn)行優(yōu)化設(shè)計(jì),提出一種基于Nginx服務(wù)器的動(dòng)態(tài)負(fù)載均衡策略,該策略由3個(gè)模塊構(gòu)成,并在算法調(diào)度模塊中給出了動(dòng)態(tài)負(fù)反饋調(diào)度算法以及不同于傳統(tǒng)算法的度量指標(biāo)。
負(fù)載均衡器是系統(tǒng)的核心,而負(fù)載均衡策略是其關(guān)鍵點(diǎn)。動(dòng)態(tài)負(fù)載均衡策略的實(shí)現(xiàn)主要由負(fù)載采集、算法調(diào)度以及健康檢查3個(gè)模塊共同完成,其中算法調(diào)度模塊的核心算法又稱為動(dòng)態(tài)負(fù)反饋調(diào)度算法。表1為各模塊的功能說(shuō)明,用戶瀏覽器發(fā)送HTTP請(qǐng)求時(shí),各個(gè)模塊工作流程如圖1所示。
表1 各模塊功能說(shuō)明Table 1 Functional description of each module
圖1 各模塊工作流程圖Fig.1 Workflow diagram for each module
當(dāng)Nginx負(fù)載均衡器接收到用戶瀏覽器發(fā)送的HTTP請(qǐng)求時(shí),會(huì)調(diào)用算法調(diào)度模塊。算法調(diào)度模塊根據(jù)采集到的負(fù)載信息通過(guò)計(jì)算找到最優(yōu)節(jié)點(diǎn),并將其返回給負(fù)載均衡器,負(fù)載均衡器會(huì)將HTTP請(qǐng)求轉(zhuǎn)發(fā)給該最優(yōu)節(jié)點(diǎn)。負(fù)載采集模塊分為負(fù)載均衡器上的Server端以及后端節(jié)點(diǎn)上的Client端(本文主要介紹Client端),當(dāng)Client端接收到Server端的采集命令后,開始采集服務(wù)器的負(fù)載信息,將其告知Server端。健康檢查模塊周期性地向后端節(jié)點(diǎn)發(fā)送HTTP健康檢查包,根據(jù)后端回復(fù)包的狀態(tài)碼來(lái)判斷健康狀態(tài),并記錄響應(yīng)時(shí)間。服務(wù)器接收到HTTP請(qǐng)求后,進(jìn)行相應(yīng)的處理,并將處理結(jié)果返回給負(fù)載均衡器,并由負(fù)載均衡器轉(zhuǎn)發(fā)給用戶瀏覽器。
2.1.1 動(dòng)態(tài)負(fù)反饋調(diào)度算法設(shè)計(jì)整體思路 通過(guò)分析Nginx內(nèi)置的輪詢r(jià)ound_robin和最小連接least_conn兩個(gè)算法可知, 算法中初始權(quán)重是由運(yùn)維人員根據(jù)經(jīng)驗(yàn)而人工設(shè)置的, 并沒(méi)有考慮到服務(wù)器性能會(huì)隨時(shí)間不斷變化, 而least_conn算法雖然引入了連接數(shù)來(lái)間接地反映節(jié)點(diǎn)的負(fù)載情況, 但在處理長(zhǎng)連接時(shí)會(huì)出現(xiàn)連接數(shù)不斷增多, 而服務(wù)器卻相對(duì)空閑的情況。 本文提出動(dòng)態(tài)負(fù)反饋調(diào)度算法——dnfs_conn算法,引入負(fù)反饋機(jī)制對(duì)節(jié)點(diǎn)進(jìn)行休眠和喚醒操作,進(jìn)而提高服務(wù)器利用率,減少能耗。
對(duì)于本文算法,節(jié)點(diǎn)的選擇是其核心部分,算法調(diào)度模塊接收到連接請(qǐng)求時(shí),會(huì)調(diào)用dnfs_conn算法獲取后端服務(wù)器集群的節(jié)點(diǎn)信息, 若當(dāng)前負(fù)載小于自定義負(fù)載下閾值wL, 使用動(dòng)態(tài)編程(dynamic programming)的方法快速遞歸找到休眠負(fù)反饋負(fù)載最小節(jié)點(diǎn), 并將其放入休眠隊(duì)列; 若大于負(fù)載上閾值wH,則喚醒節(jié)點(diǎn),繼續(xù)計(jì)算當(dāng)前負(fù)載直到負(fù)載值在兩者之間為止;如果在上、下閾值之間稱之為理想負(fù)載,不進(jìn)行處理。通過(guò)計(jì)算每個(gè)節(jié)點(diǎn)的負(fù)載值將節(jié)點(diǎn)排序,進(jìn)而選出最優(yōu)節(jié)點(diǎn),算法流程如圖2所示。
圖2 算法流程Fig.2 Algorithm flowchart
2.1.2 動(dòng)態(tài)負(fù)反饋調(diào)度算法相關(guān)參數(shù) 本文算法的相關(guān)參數(shù)主要由能夠決定服務(wù)器承載能力的靜態(tài)負(fù)載因子(static load factor)、 由服務(wù)器運(yùn)行狀態(tài)決定的、能夠反映其當(dāng)前負(fù)載情況的動(dòng)態(tài)負(fù)載因子(dynamic load factor)以及從歷史的性能數(shù)據(jù)中提取出來(lái)的統(tǒng)計(jì)類負(fù)載因子(statistical load factor)三大類組成。本文選用響應(yīng)時(shí)間和連接數(shù)作為統(tǒng)計(jì)類負(fù)載因子。算法的相關(guān)參數(shù)說(shuō)明見(jiàn)表2。
表2 相關(guān)負(fù)載因子Table 2 Relevant load factor
2.1.3 動(dòng)態(tài)負(fù)反饋調(diào)度算法的度量指標(biāo) 1)負(fù)反饋機(jī)制。 在控制系統(tǒng)中, 假設(shè)某一因素使系統(tǒng)趨于一種狀態(tài),另外一個(gè)因素使系統(tǒng)遠(yuǎn)離該狀態(tài),如果兩者中有一種或兩種是非線性的影響,就會(huì)造成平衡點(diǎn)的出現(xiàn)。為減小節(jié)點(diǎn)的負(fù)載波動(dòng),引用負(fù)反饋機(jī)制,假設(shè)在tn-1時(shí)刻節(jié)點(diǎn)的平均負(fù)載為
(1)
其中,LCPU(tn-1)、LMem(tn-1)分別為測(cè)量所得到的CPU與內(nèi)存的負(fù)載; 系數(shù)p為衡量不同應(yīng)用中CPU和內(nèi)存的影響;k為集群節(jié)點(diǎn)數(shù)。
在自動(dòng)控制理論中, 為了使系統(tǒng)能自動(dòng)適應(yīng)工作環(huán)境、 任務(wù)的變化, 采用由牛頓二項(xiàng)式定理推出的公式——開方的反饋方法(自動(dòng)調(diào)節(jié)開方)[12]自動(dòng)改變自身結(jié)構(gòu)、 參數(shù)等, 使系統(tǒng)始終保持穩(wěn)定的工作狀態(tài):
(2)
A=(x±y)i,
(3)
其中,X(m)為初始值;Q為開方次數(shù);x是估計(jì)值;y是誤差值。A值通常接近于X(m), 故最終負(fù)載為
LOAD=LOAD(tn)+(LOAD(tn)-LOAD(tn-1))1/Q。
(4)
在現(xiàn)有的負(fù)載模型中同時(shí)考慮CPU與內(nèi)存,運(yùn)用二維向量取模運(yùn)算。 同時(shí),為衡量不同應(yīng)用中CPU和內(nèi)存的不同影響, 引入系數(shù)p。 設(shè)集群中一個(gè)節(jié)點(diǎn)j的負(fù)載向量為L(zhǎng)j=〈LCPUj,LMemj〉,LCPUj、LMemj分別為節(jié)點(diǎn)j測(cè)量所得CPU和內(nèi)存負(fù)載,當(dāng)有k個(gè)節(jié)點(diǎn)時(shí), 所有節(jié)點(diǎn)在時(shí)刻tn的平均負(fù)載為
(5)
可知,LOAD(ti)可取值范圍為(0, 1),p的取值根據(jù)系統(tǒng)應(yīng)用而定。
2)抖動(dòng)系數(shù)。 抖動(dòng)現(xiàn)象指在負(fù)載變化較大時(shí), 頻繁地啟動(dòng)/暫停某一個(gè)節(jié)點(diǎn)引起的現(xiàn)象。 若同一節(jié)點(diǎn)多次遷移, 將會(huì)嚴(yán)重影響性能。 因此,如何減少抖動(dòng)現(xiàn)象的發(fā)生是當(dāng)前的首要任務(wù)。 本文算法先計(jì)算每個(gè)節(jié)點(diǎn)的實(shí)時(shí)負(fù)載
(6)
為了增強(qiáng)系統(tǒng)穩(wěn)定性,考慮節(jié)點(diǎn)抖動(dòng)系數(shù)k
(7)
其中: Δt為一個(gè)時(shí)間周期。k越大,穩(wěn)定性越差, 則加入抖動(dòng)系數(shù)后的負(fù)載為
LOADN(tn)=k×LOAD(tn)。
(8)
3)時(shí)間跨度。參考文獻(xiàn)[13], 預(yù)設(shè)在0—T的時(shí)間段可分為等長(zhǎng)的時(shí)間間隔,k段間隔的定義為{(t1-t0),(t2-t1),…,(tk-tk-1)}。由此可知,Tn表示的時(shí)間跨度是(tn-tn-1)。
4)k段時(shí)間內(nèi)節(jié)點(diǎn)的平均CPU利用率
(9)
其中,pCPUTn是CPU在Tn時(shí)間段的平均利用率。 按照這種方式節(jié)點(diǎn)的內(nèi)存利用率也可以計(jì)算。
5)響應(yīng)時(shí)間TR。 設(shè)每個(gè)請(qǐng)求均在一臺(tái)服務(wù)器上進(jìn)行, 則有
TR=sjtj,
(10)
其中,sj是服務(wù)器的請(qǐng)求次數(shù),tj是請(qǐng)求j的處理時(shí)長(zhǎng)。
6)服務(wù)器利用率φ。 設(shè)連接數(shù)為i時(shí)其穩(wěn)態(tài)概率為πi,πc+i為最大連接數(shù)時(shí)的穩(wěn)態(tài)概率, 則
(11)
其中,c為最大連接數(shù)。
7)吞吐量δ。吞吐量作為衡量服務(wù)器性能的重要指標(biāo), 可直接展現(xiàn)整個(gè)服務(wù)器處理連接請(qǐng)求的能力, 用λ表示服務(wù)率, 可表示為
(12)
2.1.4 dnfs_conn動(dòng)態(tài)負(fù)反饋調(diào)度算法 根據(jù)算法的設(shè)計(jì)思路給出具體實(shí)現(xiàn)過(guò)程的偽代碼如下:
1.輸入:所有節(jié)點(diǎn)的CPU和內(nèi)存信息
2.初始化:給每個(gè)節(jié)點(diǎn)設(shè)置特定名字
3.do
4.通過(guò)管理節(jié)點(diǎn),對(duì)各工作節(jié)能點(diǎn)根據(jù)式(5)統(tǒng)計(jì)計(jì)算得到運(yùn)行平均負(fù)載a1
5.IfwL 6.繼續(xù)執(zhí)行操作 7.else ifa1 8.將每個(gè)節(jié)點(diǎn)根據(jù)式(8)計(jì)算其負(fù)反饋負(fù)載并升序排列 9.將得到的負(fù)反饋負(fù)載節(jié)點(diǎn)休眠并放入休眠隊(duì)列,直到滿足wL 10.end for 11.else 12.將休眠隊(duì)列中的節(jié)點(diǎn)喚醒,直到wL 13.end if 14.end do 15.輸出:最佳節(jié)點(diǎn) 本文主要介紹客戶端Client上的負(fù)載采集,主要使用node.js開發(fā),通過(guò)os模塊獲取系統(tǒng)參數(shù),編寫utils模塊,用于進(jìn)行數(shù)據(jù)處理。 2.2.1 靜態(tài)負(fù)載因子采集 由2.1.2節(jié)可知,通過(guò)計(jì)算靜態(tài)負(fù)載因子SLF可以得到服務(wù)器的承載能力,因此定義getSLF方法用于采集SLF。通過(guò)os模塊獲取CPU核心數(shù)、主頻以及內(nèi)存容量,代碼如下: let getSLF=function (){ let slfCPU=os.cups()[0].speed * os.cups().length; let slfMEM=os.totalmem(); return {slfCPU,slfMEM} } 2.2.2 動(dòng)態(tài)負(fù)載因子采集 動(dòng)態(tài)負(fù)載因子的采集相對(duì)靜態(tài)的復(fù)雜一些,當(dāng)node.js執(zhí)行shell命令時(shí)所花費(fèi)的時(shí)間是無(wú)法有效統(tǒng)計(jì)的。因此,使用node.js來(lái)執(zhí)行shell腳本文件,以解決上述問(wèn)題。將采集動(dòng)態(tài)負(fù)載因子分為3部分,代碼如下: 1)getDLF將整個(gè)采集過(guò)程調(diào)用并接收結(jié)果, 代碼如下: let getDLF=function (){ let [dlfCPU,dlfMEM]=[getDlfCPU(),getDlfMEM()]; return {dlfCPU,dlfMEM} } 2)getDlfCPU通過(guò)執(zhí)行cpu.sh腳本來(lái)獲取CPU利用率,代碼如下: function getDlfCPU(){ let dlfCPU=child.exeFileSync(’./cpu.sh’).toString(); return dlfCPU; } 3)getDlfMEM內(nèi)存占用率計(jì)算,代碼如下: function getDlfMEM(){ let slfMEM=os.totalmem(); let dlfMEM=(slfMEM-os.freemem())/slfMEM; return dlfMEM;} 2.2.3 周期采集負(fù)載信息 客戶端采集負(fù)載信息具有周期性,調(diào)用setInterval方法可以在每隔time秒更新一次負(fù)載信息,核心代碼如下: let collectTimer=setInterval(()=>{ updateLoadInfo(); },time * 1000); function updateLoadInfo(){ let [SLF,DLF]=[collect.getSLF(),collect.getDLF()]; ({slfCPU,slfMEM}=SLF);({dlfCPU,dlfMEM}=DLF); L=a-cpu*dlfCPU+a-mem*dlfMEM; } 將由淘寶Tengine團(tuán)隊(duì)開發(fā)的nginx_upstream_check_module模塊添加到nginx的源碼目錄中用于檢測(cè)后端節(jié)點(diǎn)的健康狀況。使用該模塊時(shí),只需要在upstream塊中添加check指令即可,配置如下: upstream backend{ dnfs-conn; server 192.168.201:80; server 192.168.202:80; server 192.168.203:80; check interval=3000 rise=1 fall=5 timeout=1000; } 為了測(cè)試本文提出的dnfs_conn算法在高并發(fā)時(shí)負(fù)載均衡能力, 在內(nèi)網(wǎng)環(huán)境下選擇lawrence livermore national laboratory(LLNL)的數(shù)據(jù)在CPU 2.5 GHz、 8 G內(nèi)存、 Ubuntu16.04操作系統(tǒng)的平臺(tái)上用Apache HTTP服務(wù)器進(jìn)行實(shí)驗(yàn), 測(cè)試需要?jiǎng)?chuàng)建5臺(tái)云主機(jī)組成的集群, 以及一個(gè)專有網(wǎng)絡(luò)VPC(Virtual Private Cloud),其中, 1臺(tái)云主機(jī)作為Nginx負(fù)載均衡器, 3臺(tái)云主機(jī)作為Web服務(wù)器, 1臺(tái)云主機(jī)作為測(cè)試機(jī)對(duì)系統(tǒng)進(jìn)行測(cè)試。 通過(guò)不斷增加并發(fā)連接數(shù)來(lái)測(cè)試連接失敗數(shù)和平均響應(yīng)時(shí)間,驗(yàn)證本算法的可行性及優(yōu)越性并與Nginx內(nèi)置的round_robin、 least_conn這兩種算法的結(jié)果進(jìn)行比較。 根據(jù)式(5),設(shè)系數(shù)p為0.6, 上下閾值wH、wL分別為0.7和0.3, 在同一時(shí)刻訪問(wèn)服務(wù)器站點(diǎn)連接數(shù)1 500、 請(qǐng)求數(shù)15 000以下且最大持續(xù)時(shí)間超過(guò)15個(gè)時(shí)隙的情況下完成平均響應(yīng)時(shí)間、 請(qǐng)求失敗數(shù)以及負(fù)載均衡度的對(duì)比結(jié)果如圖3—5。 圖3描述了3種算法在同一時(shí)刻服務(wù)器站點(diǎn)連接數(shù)即并發(fā)數(shù)在1 500以內(nèi)的變化情況。對(duì)于round_robin算法, 連接數(shù)達(dá)到1 100以后, 平均響應(yīng)時(shí)間瞬間增加后趨于平穩(wěn); least_conn算法的平均響應(yīng)時(shí)間在并發(fā)數(shù)1 300時(shí)忽然增加到區(qū)間內(nèi)的最高值; dnfs_conn算法平均響應(yīng)時(shí)間逐漸增加且始終要低于Nginx內(nèi)置的其他兩種算法,以上表明本文提出的算法在處理高并發(fā)數(shù)的連接時(shí)明顯優(yōu)于其他算法。 圖3 平均響應(yīng)時(shí)間對(duì)比Fig.3 Comparison of average response times 圖4展示了3種算法在請(qǐng)求數(shù)為15 000以內(nèi)時(shí)失敗次數(shù)的對(duì)比情況??梢钥闯?在算法執(zhí)行過(guò)程中,當(dāng)請(qǐng)求數(shù)增加到12 000以后,雖然3種算法均出現(xiàn)失敗現(xiàn)象,但在相同并發(fā)數(shù)下發(fā)出相同請(qǐng)求數(shù)時(shí),dnfs_conn能夠成功處理的請(qǐng)求數(shù)較其他兩種算法有明顯的提高, 能夠快速響應(yīng)服務(wù)請(qǐng)求, 表明本文所提出的算法要優(yōu)于Nginx內(nèi)置的round_robin和least_conn算法。 圖4 請(qǐng)求失敗數(shù)對(duì)比Fig.4 Comparison of request failures 針對(duì)負(fù)載均衡度的對(duì)比,采用方差∑(LOADN-LOAD)2的形式, 其中LOADN為L(zhǎng)OADN(ti)在周期內(nèi)計(jì)算的平均值,LOAD為L(zhǎng)OAD(ti)在周期內(nèi)計(jì)算的平均值。為體現(xiàn)統(tǒng)計(jì)性,分別進(jìn)行5組測(cè)試來(lái)求算數(shù)平均值,比較結(jié)果如圖5所示。 圖5 方差值對(duì)比Fig.5 Comparison of square difference 可知,在最大持續(xù)時(shí)間大于60 min后,3種算法的方差值有明顯的增長(zhǎng)現(xiàn)象,但無(wú)論最大持續(xù)時(shí)間在15 min還是240 min,round_robin和least_conn兩算法的方差值相差不大; dnfs_conn算法由于每次選取節(jié)點(diǎn)的抖動(dòng)比較小,進(jìn)而使節(jié)點(diǎn)負(fù)載方差值明顯小于前兩者。 在分析Nginx服務(wù)器的內(nèi)置負(fù)載均衡策略基礎(chǔ)上,提出一種帶有動(dòng)態(tài)負(fù)反饋調(diào)度算法——dnfs_conn算法的動(dòng)態(tài)負(fù)載均衡策略, 其中dnfs_conn算法將動(dòng)態(tài)、 靜態(tài)以及統(tǒng)計(jì)類等負(fù)載因子綜合考慮, 并引入負(fù)反饋機(jī)制對(duì)節(jié)點(diǎn)進(jìn)行休眠和喚醒操作, 進(jìn)而提高服務(wù)器利用率, 減少能耗。 針對(duì)3種負(fù)載均衡策略的調(diào)度算法進(jìn)行模擬實(shí)驗(yàn), 將測(cè)試結(jié)果進(jìn)行對(duì)比分析后發(fā)現(xiàn)本文提出的負(fù)載均衡策略比Nginx內(nèi)置的round_robin和least_conn負(fù)載均衡策略有更好的均衡效果。2.2 負(fù)載采集模塊
2.3 健康檢查模塊
3 實(shí)驗(yàn)與結(jié)果分析
4 結(jié)束語(yǔ)