本文長度爲3056字,預計讀完需1.1MB流量,建議閱讀8分鐘。nginx
這篇是《分佈式關注點系列》中「負載均衡」相關的內容最後一發了,後續也會繼續講「高可用」相關的其它主題,主要是限流、降級、熔斷之類的吧,具體還沒定。文末先附上以前發過的高可用相關文章,供你再溫故一下。數據庫
下面這個場景不知是否在你面前出現過。後端
開發Z哥對運維Y弟喊:「Y弟,如今系統好卡,剛上了一波活動,趕忙幫我加幾臺機器上去頂一下。」瀏覽器
Y弟回覆說:「沒問題,分分鐘搞定」。緩存
而後就發現數據庫的壓力迅速上升,DBA就吼了:「Z哥,你丫的搞什麼呢?數據庫要被你弄垮了」。服務器
而後客服那邊接框也爆炸了,愈來愈多的用戶說剛登錄後沒多久,操做着就退出了,接着登錄,又退出了,到底還作不作生意了。微信
這些問題背後都是因爲一個「Session丟失」問題致使的。cookie
相信Session對大部分Coder來講應該都知道。它是爲了將同一個用戶的屢次訪問在系統中被識別爲「同一個用戶」而產生的概念。除此以外,還能夠基於它來減小重複往DB或者遠程服務處獲取與該用戶相關的信息,以起到提高性能的做用。網絡
在咱們作了負載均衡的場景中,若是選擇的負載策略是hash策略,那麼會使得Session產生一個反作用,這個反作用就如上面舉的案例那樣,用戶一旦因爲某種緣由從原先訪問服務器A變成訪問服務器B,就會出現「登錄狀態丟失」、「緩存穿透」等問題。session
爲何hash策略會出現這個問題呢?首先有必要先了解一下hash是如何進行的。hash策略就是下圖這樣的一個散列函數。在函數不變的狀況下,A永遠對應01,B對應04,C對應08。
▲圖片來源於網絡,版權歸原做者全部
以nginx中的ip_hash策略來舉個例子。由於咱們認爲正常狀況下用戶的ip不會在短期內發生變化,因此當咱們選擇使用ip_hash策略進行負載均衡時,意味着指望同一個用戶可以一直訪問到同一臺服務器上,就像下圖這樣。
▲圖中的hash函數是最簡單的隨意舉例
如此一來,咱們只須要在這一臺服務器上將這個用戶相關的信息緩存在進程內,就能起到很是高性價比的提高性能的效果。
這時,客戶端與服務端之間的至關於創建了一個信任,相互認識。這個信任就是「Session」。
可是,當咱們加了一臺服務器以後,事情就發生變化了。
▲圖中的hash函數是最簡單的隨意舉例
這個時候咱們原先的預期就被破壞了。由於用戶與序號0節點的連接變成了與序號3的連接,因此產生了前面提到的「Session丟失」問題。與此同時,在序號0節點上作的進程內緩存都無效了,而在序號3節點上又沒有用戶相關的任何緩存,致使大量數據須要從下游的DB或者遠程服務處獲取。你要知道,一旦涉及到網絡通訊,性能必然明顯降低,I/O、序列化都是耗時的工做。更重要的是,一旦同時有大量用戶產生這個狀況,因爲後端的DB和遠程服務瞬時沒法承載激增的高密度請求,可能會致使它掛起。這還沒完,若是當前程序沒有一些故障隔離或者降級策略,還會進一步產生蝴蝶效應,致使整個大系統響應緩慢。可謂「一顆老鼠屎壞了一鍋粥」。
既然以nginx舉例,仍是從nginx開始聊。經過在nginx中引入nginx-sticky-module模塊能夠來解決這個問題。解決的整個過程以下。
▲圖片來源於網絡,版本歸原做者全部
能夠看到,當client第一次進入到nginx匹配節點的時候,在給它分配一個節點的同時,會將這個節點的惟一標識進行md5後寫入到cookie中一併返回,若是下次再發起請求的時候發現帶有這個cookie值,就直接轉發到該值所對應的節點上去。這個機制被專業的稱之爲「Session保持」。
雖然能夠利用cookie來解決這個問題,可是cookie也有一個潛在的問題,若是客戶端未開啓cookie功能,這個機制就失效了。不過好在目前主流瀏覽器都是默認打開cookie的。
題外話:nginx是2004年發佈的,在nginx-sticky-module出現以前的7年間也是nginx相比競品HAProxy最大的一個短板,由於HAProxy支持Session保持。
除了cookie以外,還有2種方式也能夠最終達到相似的效果。分別被稱爲「Session複製」、「Session共享」。
這是最簡單粗暴的方式。根據第一節的案例來看,致使問題的緣由是節點3沒有用戶的Session。那麼很容易想到,在節點3運行以前把Session相關的Cache數據複製過去唄。而且在多個節點之間持續保證數據的同步,也就是說,每一臺節點上都存在每一個用戶的Session數據。
實現的方案有不少,特別是不一樣的宿主程序都或多或少提供了一些切入點,甚至是拿來即用的方案,如Tomcat的Delta Manager和Backup Manager、Tomcat和IIS的Filter機制等等,這裏就不展開了。
此類方案的特色是
優勢:自然高可用,一部分節點宕機沒事。由於每個節點上存放着全部已鏈接用戶的會話信息。
缺點:由於每臺計算機的內存是有上限的,僅適用於會話相關的數據大小較小的場景。而且,因爲多個節點之間須要同步數據,須要額外解決數據一致性問題。與此同時,隨着節點越多,損耗越大(延遲、帶寬等),有廣播風暴風險。
咱們還能夠經過將session信息存放到全局共享的存儲介質中來達到同樣的效果,如數據庫、遠程緩存等,這是一種中心化思想的解決方案。
此類方案的特色是
優勢:無論節點怎麼增長和減小,100%不會產生會話丟失。
缺點:每次讀寫請求都須要增長額外共享儲存調用,增長了網絡I/O、序列化等操做,性能明顯降低。另外,用做共享的存儲介質除了增長了額外的維護成本外,還須要解決單點問題,以避免產生系統性風險。
同以前「Session保持」方案一塊兒對比下各自的優缺點和適用場景。
分別用一句話歸納一下這3個方案:
Session 保持。原來在哪仍是去哪。
Session 複製。無論在哪都有同樣的數據。
Session 共享。全部節點共用一份數據。
越大型的系統,最終都會往「Session共享」這個方案上走,由於只要再對這個共享存儲作橫向擴展,理論上就能夠支撐無窮大的用戶了。如Redis、一系列的NOSQL以及NEWSQL等。就像下面這樣,集「規模大」、「高可用」、「效果好」於一身。
如今你應該清楚了Session丟失問題,也知道了如何去應對他。可是,咱們還須要明白一個事實:嚴格來講「Session保持」本質上是破壞了作「負載均衡」的初衷。舉個極端點的場景:一共有10個會話連在了節點A上,而且都是活動中狀態。那麼這個時候哪怕增長一個節點B上線,只要沒有新的會話進來,節點B上的活動鏈接數永遠是0,並無起到分擔壓力的做用。
可是,在系統的起步時期,其實用這樣簡單的方案也是極好的。
相關文章:
▶ 關於做者:張帆(Zachary, 我的微信號:Zachary-ZF)。堅持用心打磨每一篇高質量原創。
微信公衆號(首發):跨界架構師。<-- 點擊後閱讀熱門文章
按期發表原創內容:架構設計丨分佈式系統丨產品丨運營丨一些深度思考。