做了“負(fù)載均衡”就可以隨便加機(jī)器了嗎?
前面的一篇分享《如何搭建應(yīng)對(duì)億級(jí)流量的高可用負(fù)載均衡?》相信大家看完后對(duì)負(fù)載均衡的應(yīng)用有了一些了解。這篇主要為大家解答做了“負(fù)載均衡”是否能隨便加機(jī)器。
下面這個(gè)場(chǎng)景不知是否在你面前出現(xiàn)過(guò):
開(kāi)發(fā)Z哥對(duì)運(yùn)維Y弟喊:“Y弟,現(xiàn)在系統(tǒng)好卡,剛上了一波活動(dòng),趕緊幫我加幾臺(tái)機(jī)器上去頂一下。”
Y弟回復(fù)說(shuō):“沒(méi)問(wèn)題,分分鐘搞定”。
然后就發(fā)現(xiàn)數(shù)據(jù)庫(kù)的壓力迅速上升,DBA就吼了:“Z哥,你丫的搞什么呢?數(shù)據(jù)庫(kù)要被你弄垮了”。
然后客服那邊接框也爆炸了,越來(lái)越多的用戶說(shuō)剛登陸后沒(méi)多久,操作著就退出了,接著登陸,又退出了,到底還做不做生意了。
這些問(wèn)題背后都是由于一個(gè)「Session丟失」問(wèn)題導(dǎo)致的。
什么是Session丟失
相信Session對(duì)大部分Coder來(lái)說(shuō)應(yīng)該都知道。它是為了將同一個(gè)用戶的多次訪問(wèn)在系統(tǒng)中被識(shí)別為“同一個(gè)用戶”而產(chǎn)生的概念。除此之外,還可以基于它來(lái)減少重復(fù)往DB或者遠(yuǎn)程服務(wù)處獲取與該用戶相關(guān)的信息,以起到提升性能的作用。
在我們做了負(fù)載均衡的場(chǎng)景中,如果選擇的負(fù)載策略是hash策略,那么會(huì)使得Session產(chǎn)生一個(gè)副作用,這個(gè)副作用就如上面舉的案例那樣,用戶一旦由于某種原因從原先訪問(wèn)服務(wù)器A變成訪問(wèn)服務(wù)器B,就會(huì)出現(xiàn)“登陸狀態(tài)丟失”、“緩存穿透”等問(wèn)題。
為什么hash策略會(huì)出現(xiàn)這個(gè)問(wèn)題呢?首先有必要先了解一下hash是如何進(jìn)行的。hash策略就是下圖這樣的一個(gè)散列函數(shù)。在函數(shù)不變的情況下,A永遠(yuǎn)對(duì)應(yīng)01,B對(duì)應(yīng)04,C對(duì)應(yīng)08。
以nginx中的ip_hash策略來(lái)舉個(gè)例子。因?yàn)槲覀冋J(rèn)為正常情況下用戶的ip不會(huì)在短時(shí)間內(nèi)發(fā)生變化。
所以當(dāng)我們選擇使用ip_hash策略進(jìn)行負(fù)載均衡時(shí),意味著期望同一個(gè)用戶能夠一直訪問(wèn)到同一臺(tái)服務(wù)器上,就像下圖這樣:
如此一來(lái),我們只需要在這一臺(tái)服務(wù)器上將這個(gè)用戶相關(guān)的信息緩存在進(jìn)程內(nèi),就能起到非常高性價(jià)比的提升性能的效果。
這時(shí),客戶端與服務(wù)端之間的相當(dāng)于建立了一個(gè)信任,相互認(rèn)識(shí)。這個(gè)信任就是「Session」。
但是,當(dāng)我們加了一臺(tái)服務(wù)器之后,事情就發(fā)生變化了,圖中的hash函數(shù)是最簡(jiǎn)單的隨意舉例。
這個(gè)時(shí)候我們?cè)鹊念A(yù)期就被破壞了。因?yàn)橛脩襞c序號(hào)0節(jié)點(diǎn)的鏈接變成了與序號(hào)3的鏈接,所以產(chǎn)生了前面提到的「Session丟失」問(wèn)題。
與此同時(shí),在序號(hào)0節(jié)點(diǎn)上做的進(jìn)程內(nèi)緩存都無(wú)效了,而在序號(hào)3節(jié)點(diǎn)上又沒(méi)有用戶相關(guān)的任何緩存,導(dǎo)致大量數(shù)據(jù)需要從下游的DB或者遠(yuǎn)程服務(wù)處獲取。
你要知道,一旦涉及到網(wǎng)絡(luò)通信,性能必然明顯下降,I/O、序列化都是耗時(shí)的工作。
更重要的是,一旦同時(shí)有大量用戶產(chǎn)生這個(gè)情況,由于后端的DB和遠(yuǎn)程服務(wù)瞬時(shí)無(wú)法承載激增的高密度請(qǐng)求,可能會(huì)導(dǎo)致它掛起。
這還沒(méi)完,如果當(dāng)前程序沒(méi)有一些故障隔離或者降級(jí)策略,還會(huì)進(jìn)一步產(chǎn)生蝴蝶效應(yīng),導(dǎo)致整個(gè)大系統(tǒng)響應(yīng)緩慢。可謂“一顆老鼠屎壞了一鍋粥”。
Nginx如何來(lái)解決這個(gè)問(wèn)題的
既然以nginx舉例,還是從nginx開(kāi)始聊。通過(guò)在nginx中引入nginx-sticky-module模塊可以來(lái)解決這個(gè)問(wèn)題。
可以看到,當(dāng)client第一次進(jìn)入到nginx匹配節(jié)點(diǎn)的時(shí)候,在給它分配一個(gè)節(jié)點(diǎn)的同時(shí),會(huì)將這個(gè)節(jié)點(diǎn)的唯一標(biāo)識(shí)進(jìn)行md5后寫(xiě)入到cookie中一并返回。
如果下次再發(fā)起請(qǐng)求的時(shí)候發(fā)現(xiàn)帶有這個(gè)cookie值,就直接轉(zhuǎn)發(fā)到該值所對(duì)應(yīng)的節(jié)點(diǎn)上去。這個(gè)機(jī)制被專(zhuān)業(yè)的稱(chēng)之為「Session保持」。
雖然可以利用cookie來(lái)解決這個(gè)問(wèn)題,但是cookie也有一個(gè)潛在的問(wèn)題,如果客戶端未開(kāi)啟cookie功能,這個(gè)機(jī)制就失效了。不過(guò)好在目前主流瀏覽器都是默認(rèn)打開(kāi)cookie的。
題外話:nginx是2004年發(fā)布的,在nginx-sticky-module出現(xiàn)之前的7年間也是nginx相比競(jìng)品HAProxy最大的一個(gè)短板,因?yàn)镠AProxy支持Session保持。
Session保持的其它方案
除了cookie之外,還有2種方式也可以最終達(dá)到類(lèi)似的效果。分別被稱(chēng)為「Session復(fù)制」、「Session共享」。
Session復(fù)制
這是最簡(jiǎn)單粗暴的方式。根據(jù)第一節(jié)的案例來(lái)看,導(dǎo)致問(wèn)題的原因是節(jié)點(diǎn)3沒(méi)有用戶的Session。
那么很容易想到,在節(jié)點(diǎn)3運(yùn)行之前把Session相關(guān)的Cache數(shù)據(jù)復(fù)制過(guò)去唄。
并且在多個(gè)節(jié)點(diǎn)之間持續(xù)保證數(shù)據(jù)的同步,也就是說(shuō),每一臺(tái)節(jié)點(diǎn)上都存在每個(gè)用戶的Session數(shù)據(jù)。
實(shí)現(xiàn)的方案有很多,特別是不同的宿主程序都或多或少提供了一些切入點(diǎn),甚至是拿來(lái)即用的方案,如Tomcat的Delta Manager和Backup Manager、Tomcat和IIS的Filter機(jī)制等等,這里就不展開(kāi)了。
此類(lèi)方案的特點(diǎn)是:
- 優(yōu)點(diǎn):天然高可用,一部分節(jié)點(diǎn)宕機(jī)沒(méi)事。因?yàn)槊恳粋€(gè)節(jié)點(diǎn)上存放著所有已連接用戶的會(huì)話信息。
- 缺點(diǎn):因?yàn)槊颗_(tái)計(jì)算機(jī)的內(nèi)存是有上限的,僅適用于會(huì)話相關(guān)的數(shù)據(jù)大小較小的場(chǎng)景。并且,由于多個(gè)節(jié)點(diǎn)之間需要同步數(shù)據(jù),需要額外解決數(shù)據(jù)一致性問(wèn)題。與此同時(shí),隨著節(jié)點(diǎn)越多,損耗越大(延遲、帶寬等),有廣播風(fēng)暴風(fēng)險(xiǎn)。
Session共享
我們還可以通過(guò)將session信息存放到全局共享的存儲(chǔ)介質(zhì)中來(lái)達(dá)到一樣的效果,如數(shù)據(jù)庫(kù)、遠(yuǎn)程緩存等,這是一種中心化思想的解決方案。
此類(lèi)方案的特點(diǎn)是
- 優(yōu)點(diǎn):不管節(jié)點(diǎn)怎么增加和減少,100%不會(huì)產(chǎn)生會(huì)話丟失。
- 缺點(diǎn):每次讀寫(xiě)請(qǐng)求都需要增加額外共享儲(chǔ)存調(diào)用,增加了網(wǎng)絡(luò)I/O、序列化等操作,性能明顯下降。
另外,用作共享的存儲(chǔ)介質(zhì)除了增加了額外的維護(hù)成本外,還需要解決單點(diǎn)問(wèn)題,以免產(chǎn)生系統(tǒng)性風(fēng)險(xiǎn)。
同之前「Session保持」方案一起對(duì)比下各自的優(yōu)缺點(diǎn)和適用場(chǎng)景。
分別用一句話概括一下這3個(gè)方案:
- Session 保持。原來(lái)在哪還是去哪。
- Session 復(fù)制。不管在哪都有一樣的數(shù)據(jù)。
- Session 共享。所有節(jié)點(diǎn)共用一份數(shù)據(jù)。
越大型的系統(tǒng),最終都會(huì)往「Session共享」這個(gè)方案上走,因?yàn)橹灰賹?duì)這個(gè)共享存儲(chǔ)做橫向擴(kuò)展,理論上就可以支撐無(wú)窮大的用戶了。
如Redis、一系列的NOSQL以及NEWSQL等。就像下面這樣,集「規(guī)模大」、「高可用」、「效果好」于一身。
結(jié)語(yǔ)
現(xiàn)在你應(yīng)該清楚了Session丟失問(wèn)題,也知道了如何去應(yīng)對(duì)他。但是,我們還需要明白一個(gè)事實(shí):嚴(yán)格來(lái)說(shuō)「Session保持」本質(zhì)上是破壞了做「負(fù)載均衡」的初衷。舉個(gè)極端
舉個(gè)極端點(diǎn)的場(chǎng)景:一共有10個(gè)會(huì)話連在了節(jié)點(diǎn)A上,并且都是活動(dòng)中狀態(tài)。
那么這個(gè)時(shí)候哪怕增加一個(gè)節(jié)點(diǎn)B上線,只要沒(méi)有新的會(huì)話進(jìn)來(lái),節(jié)點(diǎn)B上的活動(dòng)連接數(shù)永遠(yuǎn)是0,并沒(méi)有起到分擔(dān)壓力的作用。但是,在系統(tǒng)的起步時(shí)期,其實(shí)用這樣簡(jiǎn)單的方案也是極好的。