您當前的位置:首頁 > 攝影

2.1,高效能/高可用:負載均衡叢集概述(一)

作者:由 方小蔥 發表于 攝影時間:2018-06-27

當我們在討論負載均衡的時候我們在討論什麼?

上文中理清了一些基本概念,本文將著重討論"高效能""高可用"這2個目標的其中一種解決方案:

負載均衡叢集;

那麼

什麼是負載均衡叢集?為什麼需要負載均衡?什麼場景下需要做負載均衡?負載均衡存問題和風險又在哪裡?如何做負載均衡?負載均衡的關注點?

什麼是負載均衡叢集?

其實這個高大上的名詞包含的意思其實非常簡單:"叢集"和"負載均衡"~

假設你辦了一家網站,為使用者提供新聞資訊服務,剛起步的時候只有幾百使用者,我們從成本出發準備一臺虛擬機器就能解決問題;但是隨著網站訪問量的增加,伺服器資源耗盡,使用者反饋訪問緩慢;需要兩臺或者更多的機器才能提供相同質量的服務,此時最節省成本的做法是再買2臺機器;3臺伺服器分擔請求,理論上速度可以比原來快2倍(是原來的三倍)。

先說說叢集:

單機算力不足或者無法滿足當前業務的情況下,簡單的堆疊機器是最廉價的解決方案,很多時候只需要簡單改動甚至不需要改就能對一個系統進行多機部署,實現一個叢集;

+————————-+

+——————+ | |

| | | Tomcat1 |

| +——————> |

| NGINX | +————————-+

| |

| upstream | +————————-+

| | | |

| +——————> Tomcat2 |

+——————+ | |

+————————-+

上面的就是一個簡單的tomcat叢集~

其中tomcat1和tomcat2部署相同的應用~

為什麼需要負載均衡叢集?

上面說了,單機算力不足無法滿足業務需求,所以需要叢集;叢集之間伺服器各負載一部分業務請求,為了整體業務的平衡(不至於造成一部分資源佔用嚴重,一部分資源空閒而浪費)的情況所以需要"均衡"~好吧~可能不夠嚴謹~但我覺得我是說明白了的~

什麼場景下使用負載均衡?

1,高訪問量高負載業務:對於上面的情況我們遇到的問題是單機無法支撐大量互相不關聯的請求的時候,我們透過擴充套件N個tomcat的來實現"效能"上的提升~

2,為了提升可用性,避免單點故障;

3,同城/異地冗災~

*負載均衡的問題和風險在哪裡?

1,

共享資料問題

:就拿上面場景來說:我們知道http是無狀態的,可往往大規模的需要叢集的系統99。9999%的情況都是有狀態的;尤其是帶有頁面的web程式,往往需要記錄使用者當前的會話資訊(session),常規的做法是將一個sessionid寫入cookie或者帶再url後面~然後來回的傳送他們以維持使用者的狀態;我們知道每個tomcat自己有session manager,預設的session manager實現是將會話資料放在記憶體中,這就意味著tomcat的會話資料是不共享的;tomcat前面的NGX如果不知道後端具體業務的情況,就會發生問題:比如使用者登陸了,重新整理一下頁面就退出了之類的(實際上使用者確實登入了,只是第二次被傳送到了另一個沒有登入的tomcat上去了)~處理這種問題一般有以下三種思路:

a,session共享:實現自定義的session manager替換掉tomcat"基於記憶體的default session manager",將session資料存放在共享的區域內,比如後端資料庫,網路檔案系統,memcached(早期推薦的方案)/redis(貌似現在大家都喜歡用這個)甚至ZooKeeper。等...對於其他共享資料,比如快取,也可以這麼來實現~

b,session複製:N個tomcat之間的session相互複製,每個機器上都存在一份相同的session;

這種思路相對來說成本會更高一些

,尤其是tomcat非常的多的情況,效能下降會非常快~

c,在代理層離出sessionId,然後根據特定的sessionId路由到特定的tomcat;既:使用者A始終會被路由到同一個tomcat上去~這裡對"路由策略"存在一定的侵略性,

很容易造成系統叢集的"不均衡"

(你無法預料使用者是)

其中"session共享"是目前大家普遍採用的方案,實際上實現實現"共享"並不困難,很早以前就有基於memcached的session共享方案了~github上也能找到redis的實現~當然你如果不滿意自己實現一個也不是一個很困難的事情~

那麼問題在哪裡的呢?

這裡存在一個坑:序列化問題~

如果你的系統是面向單機開發的,剛開始的時候可能並未考慮到需要部署到多個tomcat的情況,導致你在開發的時候很多放入session的資料沒有實現序列化介面~

這些資料在單機部署的時候會被存入記憶體,所以不會存在任何問題,可如果這些資料需要在"網路""檔案"中傳輸儲存的時候就必須能夠序列化~一些老舊的系統往往因為沒注意這個問題導致一些奇怪的問題~

一般而言,你都需要在系統開發前叮囑一遍你的開發人員"存入session的資料必須可以序列化"~

2,資源隔離粒度不夠問題:

怎麼理解這個事情呢?上文中說到一個"收藏夾"和"訂單系統"競爭資源的問題:假設你的系統把這些業務都放在一個程序內實現~收藏夾和訂單業務放在一起~當你的系統負載很高,訪問量很大的時候可能出現"重要性低但使用率高"的收藏夾業務搶佔了"重要性高的"訂單業務(如果大量的人正在使用收藏夾,可能會導致訂單業務無法正常使用)~

後期說的"分散式計算(也就是服務化)"就能很好的做到資源隔離~使得某些系統支撐不住的時候核心業務依然可以正常開展~但相應的成本也會增加~

如何做實現負載均衡叢集?

這裡不會告訴你具體如何實現一個系統的負載均衡~這些配置性的東西你或許可以根據自己的需求自己搜尋得到~這裡只討論思路,討論在你面臨一些問題的時候能快速想到應該在哪個層面解決更好~

多機場景業務或系統之間協調互動最常用的方法是透過"網路";所以對於叢集的實現我們往往會從"網路"層面入手~

上圖中這兩個箭頭是負載均衡很關鍵的部分,對於上述場景,NGX做反向代理,根據策略將請求傳送到特定的後端tomcats,這裡使用相同或者不同的協議,比如http->http,https->http,早在httpd時代我們使用mod_jk來提高效率~到現在tomcat的8009埠預設還是開著的~有興趣可以看一下AJP協議(很多關於tomcat原始碼解析的書籍都會提到這個東西)~

一,資料路由

路由或者線路切換問題是負載均衡的核心;(如何根據業務(這很重要)來實現路由策略是一個很大的難題;這需要考慮上面的"均衡問題"以及下面的"節點增加或者丟失重新均衡",總而言之理由要做的不僅僅是簡單的線路上的問題。)

當然這裡講的就只是線路切換問題:

注意:線路切換往往依賴於網路;

A,基於解析服務(中介)

請求往往不會直接到達前置伺服器,更多的時候我們使用DNS,他是一箇中間人的角色,透過解析一個域名獲得前置伺服器的具體IP,隨後再直接基於解析出來的IP地址;再比如dubbo的註冊中心我們可以認為是一箇中間人;

真正請求發出前我們先問問中間人應該走那條路線

B,物理線路切換

簡單粗暴的將網線拔下來,插在另一臺相同伺服器上是簡單粗暴解決可用性的一個好方法,

呵呵(手動笑而不語)

然而你不可能做到一秒鐘內插拔200次甚至更多;當然你可以說我在光纖種使用不同長度的波,或者無線網路,我基於不同的頻段或者頻道來做(實際上這種方式並不能很好的用於此種場景);就目前而言物理層線路切換的代價太高了。

C,三/四層的轉發

多數情況下基於IP層的轉發是效率相對最好也是最普遍,基本硬體負載或者基於IPVS(LVS)的負載只需解析重新封裝三/四層的協議,他不會產生產生流量,也不需要維護兩份連線控制代碼(七層轉發種會提到),如果基於LVS軟負載,基於linux net filter實現的轉發,只需在linux核心層工作,避免了核心層和使用者層的記憶體複製所帶來的開銷,甚至一臺廉價的戴爾PC伺服器就能穩定的到達10W的connection,沒有足夠的資金購買硬體負載,而訪問量極高的系統建議採用此種方式。

D,七層轉發

所謂的七層協議,就是應用層協議(有的書上會說五層 what ever?你懂了就行?)

HTTP協議就是第七層的協議,mysql伺服器或者redis自己實現的協議也是第七層協議;使用nNginx或者Httpd解析七層協議,然後rewrite/upstream是目前主流的解決辦法;

這種方法設定簡單,維護方便,可自定義程度高。相比於LVS我相信絕大多數系統工程師對NGX會更加熟悉一些,這就帶來了設定接單維護成本低,出了問題好解決;

相比第三四層的協議,七層可以得到最大靈活度,解析HTTP協議獲得URL獲得使用者傳遞過來某一個引數(比如sessionid等),簡單的指令碼或者正則就能定義個性化的轉發策略。同樣MYSQL proxy,AMIBA這種代理中介軟體透過解析MYSQL協議獲得傳送的SQL,透過簡單的指令碼語言(比如mysql 使用lua。。。)可以很靈活的更具SQL的內容來確定具體的後方伺服器,比如讀寫分離;

需要注意的是:七層轉發會產生流量的,同時七層代理伺服器需要維護前後兩個連線控制代碼(收到使用者請求產生並維持一個服務連線,同時作為客戶端向後端服務發起請求是另外一個連線),這種轉發模式一般在使用者層實現,系統需要將核心層的資料複製到使用者空間,完成解析和處理,再將使用者空間的資料複製到核心空間,由linux的網路子系統轉發,所以我們說他會產生流量,相比三四層的轉發其代價就要高很多。

E,基於客戶端的路由

我們在客戶端實現請求具體去哪一個伺服器,透過配置一個伺服器叢集列表或者從"名字伺服器"查詢和下載這個列表,然後由客戶端決定具體選擇哪條線路的路由。redis官方的"哨兵"方案就是這種模式。這種方式相比中介軟體(代理/轉發)的方式的代價就是在一定程度上改動一些程式碼;

綜述:

DNS輪詢屬於中間人模式,dubbo同樣採用中間人方式;三四七層轉發屬於代理模式(Proxy);基於使用者選擇實際上是講路由的功能放到客戶端實現,比如淘寶的資料中介軟體等採用了這種模式(dubbo是向註冊中心獲取服務提供者列表並且儲存的,策略在註冊中心實現,同樣伺服器提供者的負載策略也不完全取決於服務的消費者);

選擇四層轉發(交換)還是七層不僅僅取決於效能需求,更多的還是取決於功能需求和成本

,比如你的系統要求透過URL的某個關鍵字或者引數來決定具體線路的,你可能就必須得選擇"七層裝置"來做代理了。

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

二,如何均衡?

我們有兩臺一毛一樣的伺服器,我們更多的是希望於兩臺伺服器分擔相同量的工作,而不是一臺伺服器很忙,而另一臺伺服器翹著二郎腿喝咖啡~

這往往很難,均衡問題不僅僅取決於使用的方法或者策略,更取決於你對問題的預判,還有對現實問題的妥協。

唯一的做法根據業務是不斷的調整,積累經驗逐步調優。

最基本也是最簡單的均衡策略是:隨機,當然還有輪詢,基於後端最少使用或者人為權重;

A,隨機(Random):

不考慮實際情況,將問題或者請求隨機的拋給兩臺伺服器,就像跑硬幣實驗一樣,訪問量越高,就越均衡。

B,輪詢(Round Robin):

不考慮後端伺服器的實際情況,ABCABCABCABCABC。。。的方式依次給A,B,C三臺伺服器;可以很簡單使用"求餘"來得到這樣的效果;

C,最少使用(LeastUse/LeastActive)

伺服器選擇相對空閒的機器最為下一個請求的處理者。這相對要複雜一些,因為實現這個策略需要負載裝置和ABC三臺臺伺服器通訊,得到AB和C的資源使用情況,當然對每個後端的服務維護一個簡單的計數器,排程最小活躍的機器也是一個很好的辦法。

D,人為權重

本質上和隨機沒什麼區別只是加入了人為因子(我們可以理解隨機策略往往得不到理想中均衡,需要人為設定一些因子作為調整,從而得到一個相對均衡)

E,HASH

使用hash或者一致性hash是一種很好策略。

F,你可以按照你的喜好和場景實現不同的策略~

標簽: 負載  伺服器  均衡  tomcat  session