深入淺出負載均衡

{"type":"doc","content":[{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"一、負載均衡簡介","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1.1. 大型網站面臨的挑戰","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"大型網站都要面對龐大的用戶量,高併發,海量數據等挑戰。爲了提升系統整體的性能,可以採用垂直擴展和水平擴展兩種方式。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"垂直擴展","attrs":{}},{"type":"text","text":":在網站發展早期,可以從單機的角度通過增加硬件處理能力,比如 CPU 處理能力,內存容量,磁盤等方面,實現服務器處理能力的提升。但是,單機是有性能瓶頸的,一旦觸及瓶頸,再想提升,付出的成本和代價會極高。這顯然不能滿足大型分佈式系統(網站)所有應對的大流量,高併發,海量數據等挑戰。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"水平擴展","attrs":{}},{"type":"text","text":":通過集羣來分擔大型網站的流量。集羣中的應用服務器(節點)通常被設計成無狀態,用戶可以請求任何一個節點,這些節點共同分擔訪問壓力。水平擴展有兩個要點:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"應用集羣:將同一應用部署到多臺機器上,組成處理集羣,接收負載均衡設備分發的請求,進行處理,並返回相應數據。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"負載均衡:將用戶訪問請求,通過某種算法,分發到集羣中的節點。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1.2. 什麼是負載均衡","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"負載均衡(Load Balance,簡稱 LB)是高併發、高可用系統必不可少的關鍵組件,目標是 盡力將網絡流量平均分發到多個服務器上,以提高系統整體的響應速度和可用性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"負載均衡的主要作用如下:","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"高併發:","attrs":{}},{"type":"text","text":"負載均衡通過算法調整負載,盡力均勻的分配應用集羣中各節點的工作量,以此提高應用集羣的併發處理能力(吞吐量)。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"伸縮性:","attrs":{}},{"type":"text","text":"添加或減少服務器數量,然後由負載均衡進行分發控制。這使得應用集羣具備伸縮性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"高可用:","attrs":{}},{"type":"text","text":"負載均衡器可以監控候選服務器,當服務器不可用時,自動跳過,將請求分發給可用的服務器。這使得應用集羣具備高可用的特性。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"安全防護:","attrs":{}},{"type":"text","text":"有些負載均衡軟件或硬件提供了安全性功能,如:黑白名單處理、防火牆,防 DDos 攻擊等。","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"二、負載均衡的分類","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"支持負載均衡的技術很多,我們可以通過不同維度去進行分類。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.1 載體維度分類","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從支持負載均衡的載體來看,可以將負載均衡分爲兩類:","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"硬件負載均衡、軟件負載均衡","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.1.1硬件負載均衡","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"硬件負載均衡,一般是在定製處理器上運行的獨立負載均衡服務器,價格昂貴,土豪專屬。硬件負載均衡的主流產品有:F5 和 A10。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"硬件負載均衡的 優點:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"功能強大:支持全局負載均衡並提供較全面的、複雜的負載均衡算法。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"性能強悍:硬件負載均衡由於是在專用處理器上運行,因此吞吐量大,可支持單機百萬以上的併發。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"安全性高:往往具備防火牆,防 DDos 攻擊等安全功能。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"硬件負載均衡的 缺點:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"成本昂貴:購買和維護硬件負載均衡的成本都很高。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"擴展性差:當訪問量突增時,超過限度不能動態擴容。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.1.2 軟件負載均衡","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"軟件負載均衡,應用最廣泛,無論大公司還是小公司都會使用。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"軟件負載均衡從軟件層面實現負載均衡,一般可以在任何標準物理設備上運行。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"軟件負載均衡的 主流產品 有:","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Nginx、HAProxy、LVS","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LVS 可以作爲四層負載均衡器。其負載均衡的性能要優於 Nginx。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HAProxy 可以作爲 HTTP 和 TCP 負載均衡器。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"Nginx、HAProxy 可以作爲四層或七層負載均衡器。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"軟件負載均衡的 優點:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"擴展性好:適應動態變化,可以通過添加軟件負載均衡實例,動態擴展到超出初始容量的能力。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"成本低廉:軟件負載均衡可以在任何標準物理設備上運行,降低了購買和運維的成本。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"軟件負載均衡的 缺點:","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"性能略差:相比於硬件負載均衡,軟件負載均衡的性能要略低一些。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2.2 網絡通信分類","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"軟件負載均衡從通信層面來看,又可以分爲四層和七層負載均衡。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1) ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"七層負載均衡","attrs":{}},{"type":"text","text":":就是可以根據訪問用戶的 HTTP 請求頭、URL 信息將請求轉發到特定的主機。","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DNS 重定向","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP 重定向","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"反向代理","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2) ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"四層負載均衡","attrs":{}},{"type":"text","text":":基於 IP 地址和端口進行請求的轉發。","attrs":{}}]},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"修改 IP 地址","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"修改 MAC 地址","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.2.1 DNS 負載均衡","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DNS 負載均衡一般用於互聯網公司,複雜的業務系統不適合使用。大型網站一般使用 DNS 負載均衡作爲 第一級負載均衡手段,然後在內部使用其它方式做第二級負載均衡。DNS 負載均衡屬於七層負載均衡。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DNS 即 域名解析服務,是 OSI 第七層網絡協議。DNS 被設計爲一個樹形結構的分佈式應用,自上而下依次爲:根域名服務器,一級域名服務器,二級域名服務器,... ,本地域名服務器。顯然,如果所有數據都存儲在根域名服務器,那麼 DNS 查詢的負載和開銷會非常龐大。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此,DNS 查詢相對於 DNS 層級結構,是一個逆向的遞歸流程,DNS 客戶端依次請求本地 DNS 服務器,上一級 DNS 服務器,上上一級 DNS 服務器,... ,根 DNS 服務器(又叫權威 DNS 服務器),一旦命中,立即返回。爲了減少查詢次數,每一級 DNS 服務器都會設置 DNS 查詢緩存。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DNS 負載均衡的工作原理就是:基於 DNS 查詢緩存,按照負載情況返回不同服務器的 IP 地址。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/43/436f20a02681c457d94088c5b52d8c70.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DNS 重定向的 優點:","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"使用簡單:負載均衡工作,交給 DNS 服務器處理,省掉了負載均衡服務器維護的麻煩","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"提高性能:可以支持基於地址的域名解析,解析成距離用戶最近的服務器地址(類似 CDN 的原理),可以加快訪問速度,改善性能;","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"DNS 重定向的 缺點:","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可用性差:DNS 解析是多級解析,新增/修改 DNS 後,解析時間較長;解析過程中,用戶訪問網站將失敗;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"擴展性低:DNS 負載均衡的控制權在域名商那裏,無法對其做更多的改善和擴展;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"維護性差:也不能反映服務器的當前運行狀態;支持的算法少;不能區分服務器的差異(不能根據系統與服務的狀態來判斷負載)。","attrs":{}}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.2.2 HTTP 負載均衡","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP 負載均衡是基於 HTTP 重定向實現的。HTTP 負載均衡屬於七層負載均衡。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP 重定向原理是:根據用戶的 HTTP 請求計算出一個真實的服務器地址,將該服務器地址寫入 HTTP 重定向響應中,返回給瀏覽器,由瀏覽器重新進行訪問。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/4f/4f2d1678bd2b93ee4d41964348ac5c45.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP 重定向的優點:方案簡單。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"HTTP 重定向的 缺點:","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"性能較差:每次訪問需要兩次請求服務器,增加了訪問的延遲。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"降低搜索排名:使用重定向後,搜索引擎會視爲 SEO 作弊。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果負載均衡器宕機,就無法訪問該站點。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於其缺點比較明顯,所以這種負載均衡策略實際應用較少。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.2.3 反向代理負載均衡","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"反向代理(Reverse Proxy)方式是指以 代理服務器 來接受網絡請求,然後 將請求轉發給內網中的服務器,並將從內網中的服務器上得到的結果返回給網絡請求的客戶端。反向代理負載均衡屬於七層負載均衡。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"反向代理服務的主流產品:","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"Nginx、Apache","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正向代理與反向代理有什麼區別?","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"正向代理:發生在 客戶端,是由用戶主動發起的。翻牆軟件就是典型的正向代理,客戶端通過主動訪問代理服務器,讓代理服務器獲得需要的外網數據,然後轉發回客戶端。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"反向代理:發生在 服務端,用戶不知道代理的存在。","attrs":{}}]}],"attrs":{}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/1a/1a604ba54f4121f7ed625e5d124cfe35.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"反向代理是如何實現負載均衡的呢?以 Nginx 爲例,如下所示:","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/50/50aae959161035ed1ed6404a6bf573da.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"首先,在代理服務器上設定好負載均衡規則。然後,當收到客戶端請求,反向代理服務器攔截指定的域名或 IP 請求,根據負載均衡算法,將請求分發到候選服務器上。其次,如果某臺候選服務器宕機,反向代理服務器會有容錯處理,比如分發請求失敗 3 次以上,將請求分發到其他候選服務器上。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"反向代理的 優點:","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1) 多種負載均衡算法:支持多種負載均衡算法,以應對不同的場景需求。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2) 可以監控服務器:基於 HTTP 協議,可以監控轉發服務器的狀態,如:系統負載、響應時間、是否可用、連接數、流量等,從而根據這些數據調整負載均衡的策略。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"反向代理的 缺點:","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1) 額外的轉發開銷:反向代理的轉發操作本身是有性能開銷的,可能會包括創建連接,等待連接響應,分析響應結果等操作。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2) 增加系統複雜度:反向代理常用於做分佈式應用的水平擴展,但反向代理服務存在以下問題,爲了解決以下問題會給系統整體增加額外的複雜度和運維成本:","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"反向代理服務如果自身宕機,就無法訪問站點,所以需要有 高可用 方案,常見的方案有:主備模式(一主一備)、雙主模式(互爲主備)。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"},"content":[{"type":"text","text":"反向代理服務自身也存在性能瓶頸,隨着需要轉發的請求量不斷攀升,需要有 可擴展 方案。","attrs":{}}]}]}],"attrs":{}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.2.4 IP負載均衡","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"IP 負載均衡是在網絡層通過修改請求目的地址進行負載均衡。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e6/e6b307e7492c6b7ee3149fbccb8d8c1e.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如上圖所示,IP 均衡處理流程大致爲:","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"客戶端請求 192.168.137.10,由負載均衡服務器接收到報文。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"負載均衡服務器根據算法選出一個服務節點 192.168.0.1,然後將報文請求地址改爲該節點的 IP。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"真實服務節點收到請求報文,處理後,返回響應數據到負載均衡服務器。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"負載均衡服務器將響應數據的源地址改負載均衡服務器地址,返回給客戶端。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"IP 負載均衡在內核進程完成數據分發,較反向代理負載均衡有更好的從處理性能。但是,由於所有請求響應都要經過負載均衡服務器,集羣的吞吐量受制於負載均衡服務器的帶寬。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"2.2.5 數據鏈路層負載均衡","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"數據鏈路層負載均衡是指在通信協議的數據鏈路層修改 mac 地址進行負載均衡。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/28/28e74446bf201c13805d2687e92de38c.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 Linux 平臺上最好的鏈路層負載均衡開源產品是 LVS (Linux Virtual Server)。LVS 是基於 Linux 內核中 netfilter 框架實現的負載均衡系統。netfilter 是內核態的 Linux 防火牆機制,可以在數據包流經過程中,根據規則設置若干個關卡(hook 函數)來執行相關的操作。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"LVS 的工作流程大致如下:","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當用戶訪問 www.sina.com.cn 時,用戶數據通過層層網絡,最後通過交換機進入 LVS 服務器網卡,並進入內核網絡層。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"進入 PREROUTING 後經過路由查找,確定訪問的目的 VIP 是本機 IP 地址,所以數據包進入到 INPUT 鏈上;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"IPVS 是工作在 INPUT 鏈上,會根據訪問的 ","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"vip+port","attrs":{}},{"type":"text","text":" 判斷請求是否 IPVS 服務,如果是則調用註冊的 IPVS HOOK 函數,進行 IPVS 相關主流程,強行修改數據包的相關數據,並將數據包發往 POSTROUTING 鏈上;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"POSTROUTING 上收到數據包後,根據目標 IP 地址(後端服務器),通過路由選路,將數據包最終發往後端的服務器上。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"開源 LVS 版本有 3 種工作模式,每種模式工作原理截然不同,說各種模式都有自己的優缺點,分別適合不同的應用場景,不過最終本質的功能都是能實現均衡的流量調度和良好的擴展性。主要包括三種模式:DR 模式、NAT 模式、Tunnel 模式。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"三、負載均衡算法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"負載均衡器的實現可以分爲兩個部分:","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"根據負載均衡算法在候選服務器列表選出一個服務器;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":"br"}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"將請求數據發送到該服務器上。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"負載均衡算法是負載均衡服務核心中的核心。負載均衡產品多種多樣,但是各種負載均衡算法原理是共性的。負載均衡算法有很多種,分別適用於不同的應用場景,本文僅介紹最爲常見的負載均衡算法的特性及原理:","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"輪詢、隨機、最小活躍數、源地址哈希、一致性哈希","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"注:","attrs":{}},{"type":"text","text":"負載均衡算法的實現,推薦閱讀 ","attrs":{}},{"type":"link","attrs":{"href":"https://dubbo.apache.org/zh/docs/v2.7/dev/source/loadbalance/","title":null,"type":null},"content":[{"type":"text","text":"Dubbo 官方負載均衡算法說明","attrs":{}}]},{"type":"text","text":" ,源碼講解非常詳細,非常值得借鑑。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3.1 隨機","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"3.1.1 隨機算法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"隨機(Random)","attrs":{}},{"type":"text","text":" 算法將請求隨機分發到候選服務器。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"隨機算法 適合服務器硬件相同的場景。學習過概率論的都知道,調用量較小的時候,可能負載並不均勻,調用量越大,負載越均衡。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/db/db8a87707531558572c5dbf982774882.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"【示例】隨機算法實現示例","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"負載均衡接口","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public interface LoadBalance {\n\n N select(List nodes, String ip);\n\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"負載均衡抽象類","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public abstract class BaseLoadBalance implements LoadBalance {\n\n @Override\n public N select(List nodes, String ip) {\n if (CollectionUtil.isEmpty(nodes)) {\n return null;\n }\n\n // 如果 nodes 列表中僅有一個 node,直接返回即可,無需進行負載均衡\n if (nodes.size() == 1) {\n return nodes.get(0);\n }\n\n return doSelect(nodes, ip);\n }\n\n protected abstract N doSelect(List nodes, String ip);\n\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"服務器節點類","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class Node implements Comparable {\n\n protected String url;\n\n protected Integer weight;\n\n protected Integer active;\n\n // ...\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"隨機算法實現","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class RandomLoadBalance extends BaseLoadBalance implements LoadBalance {\n\n private final Random random = new Random();\n\n @Override\n protected N doSelect(List nodes, String ip) {\n // 在列表中隨機選取一個節點\n int index = random.nextInt(nodes.size());\n return nodes.get(index);\n }\n\n}","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"3.1.2 加權隨機算法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"加權隨機(Weighted Random","attrs":{}},{"type":"text","text":") 算法在隨機算法的基礎上,按照概率調整權重,進行負載分配。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"【示例】加權隨機算法實現示例","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class WeightRandomLoadBalance extends BaseLoadBalance implements LoadBalance {\n\n private final Random random = ThreadLocalRandom.current();\n\n @Override\n protected N doSelect(List nodes, String ip) {\n\n int length = nodes.size();\n AtomicInteger totalWeight = new AtomicInteger(0);\n for (N node : nodes) {\n Integer weight = node.getWeight();\n totalWeight.getAndAdd(weight);\n }\n\n if (totalWeight.get() > 0) {\n int offset = random.nextInt(totalWeight.get());\n for (N node : nodes) {\n // 讓隨機值 offset 減去權重值\n offset -= node.getWeight();\n if (offset < 0) {\n // 返回相應的 Node\n return node;\n }\n }\n }\n\n // 直接隨機返回一個\n return nodes.get(random.nextInt(length));\n }\n\n}","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3.2 輪詢","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"3.2.1 輪詢算法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"輪詢(Round Robin)","attrs":{}},{"type":"text","text":"算法的策略是:將請求依次分發到候選服務器。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如下圖所示,負載均衡器收到來自客戶端的 6 個請求,(1, 3, 5) 的請求會被髮送到服務器 1,(2, 4, 6) 的請求會被髮送到服務器 2。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/c1/c19e290329384726b3f3379f6eec250d.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"該算法適合場景:各服務器處理能力相近,且每個事務工作量差異不大。如果存在較大差異,那麼處理較慢的服務器就可能會積壓請求,最終無法承擔過大的負載。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/33/3332a6f9a33847df3e7bf08cc55ccbb2.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"【示例】輪詢算法示例","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"輪詢負載均衡算法實現","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class RoundRobinLoadBalance extends BaseLoadBalance implements LoadBalance {\n\n private final AtomicInteger position = new AtomicInteger(0);\n\n @Override\n protected N doSelect(List nodes, String ip) {\n int length = nodes.size();\n // 如果位置值已經等於節點數,重置爲 0\n position.compareAndSet(length, 0);\n N node = nodes.get(position.get());\n position.getAndIncrement();\n return node;\n }\n\n}","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"3.2.2 加權輪詢算法","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"加權輪詢(Weighted Round Robbin)","attrs":{}},{"type":"text","text":"算法在輪詢算法的基礎上,增加了權重屬性來調節轉發服務器的請求數目。性能高、處理速度快的節點應該設置更高的權重,使得分發時優先將請求分發到權重較高的節點上。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如下圖所示,服務器 A 設置權重爲 5,服務器 B 設置權重爲 1,負載均衡器收到來自客戶端的 6 個請求,那麼 (1, 2, 3, 4, 5) 請求會被髮送到服務器 A,(6) 請求會被髮送到服務器 B。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/07/07072e320d6d5d2ca38b2942b71e465a.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"【示例】加權輪詢算法實現示例","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以下實現基於 Dubbo 加權輪詢算法做了一些簡化。","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class WeightRoundRobinLoadBalance extends BaseLoadBalance implements LoadBalance {\n\n /**\n * 60秒\n */\n private static final int RECYCLE_PERIOD = 60000;\n\n /**\n * Node hashcode 到 WeightedRoundRobin 的映射關係\n */\n private ConcurrentMap weightMap = new ConcurrentHashMap<>();\n\n /**\n * 原子更新鎖\n */\n private AtomicBoolean updateLock = new AtomicBoolean();\n\n @Override\n protected N doSelect(List nodes, String ip) {\n\n int totalWeight = 0;\n long maxCurrent = Long.MIN_VALUE;\n\n // 獲取當前時間\n long now = System.currentTimeMillis();\n N selectedNode = null;\n WeightedRoundRobin selectedWRR = null;\n\n // 下面這個循環主要做了這樣幾件事情:\n // 1. 遍歷 Node 列表,檢測當前 Node 是否有相應的 WeightedRoundRobin,沒有則創建\n // 2. 檢測 Node 權重是否發生了變化,若變化了,則更新 WeightedRoundRobin 的 weight 字段\n // 3. 讓 current 字段加上自身權重,等價於 current += weight\n // 4. 設置 lastUpdate 字段,即 lastUpdate = now\n // 5. 尋找具有最大 current 的 Node,以及 Node 對應的 WeightedRoundRobin,\n // 暫存起來,留作後用\n // 6. 計算權重總和\n for (N node : nodes) {\n int hashCode = node.hashCode();\n WeightedRoundRobin weightedRoundRobin = weightMap.get(hashCode);\n int weight = node.getWeight();\n if (weight < 0) {\n weight = 0;\n }\n\n // 檢測當前 Node 是否有對應的 WeightedRoundRobin,沒有則創建\n if (weightedRoundRobin == null) {\n weightedRoundRobin = new WeightedRoundRobin();\n // 設置 Node 權重\n weightedRoundRobin.setWeight(weight);\n // 存儲 url 唯一標識 identifyString 到 weightedRoundRobin 的映射關係\n weightMap.putIfAbsent(hashCode, weightedRoundRobin);\n weightedRoundRobin = weightMap.get(hashCode);\n }\n // Node 權重不等於 WeightedRoundRobin 中保存的權重,說明權重變化了,此時進行更新\n if (weight != weightedRoundRobin.getWeight()) {\n weightedRoundRobin.setWeight(weight);\n }\n\n // 讓 current 加上自身權重,等價於 current += weight\n long current = weightedRoundRobin.increaseCurrent();\n // 設置 lastUpdate,表示近期更新過\n weightedRoundRobin.setLastUpdate(now);\n // 找出最大的 current\n if (current > maxCurrent) {\n maxCurrent = current;\n // 將具有最大 current 權重的 Node 賦值給 selectedNode\n selectedNode = node;\n // 將 Node 對應的 weightedRoundRobin 賦值給 selectedWRR,留作後用\n selectedWRR = weightedRoundRobin;\n }\n\n // 計算權重總和\n totalWeight += weight;\n }\n\n // 對 weightMap 進行檢查,過濾掉長時間未被更新的節點。\n // 該節點可能掛了,nodes 中不包含該節點,所以該節點的 lastUpdate 長時間無法被更新。\n // 若未更新時長超過閾值後,就會被移除掉,默認閾值爲60秒。\n if (!updateLock.get() && nodes.size() != weightMap.size()) {\n if (updateLock.compareAndSet(false, true)) {\n try {\n // 遍歷修改,即移除過期記錄\n weightMap.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > RECYCLE_PERIOD);\n } finally {\n updateLock.set(false);\n }\n }\n }\n\n if (selectedNode != null) {\n // 讓 current 減去權重總和,等價於 current -= totalWeight\n selectedWRR.decreaseCurrent(totalWeight);\n // 返回具有最大 current 的 Node\n return selectedNode;\n }\n\n // should not happen here\n return nodes.get(0);\n }\n\n protected static class WeightedRoundRobin {\n\n // 服務提供者權重\n private int weight;\n // 當前權重\n private AtomicLong current = new AtomicLong(0);\n // 最後一次更新時間\n private long lastUpdate;\n\n public long increaseCurrent() {\n // current = current + weight;\n return current.addAndGet(weight);\n }\n\n public long decreaseCurrent(int total) {\n // current = current - total;\n return current.addAndGet(-1 * total);\n }\n\n public int getWeight() {\n return weight;\n }\n\n public void setWeight(int weight) {\n this.weight = weight;\n // 初始情況下,current = 0\n current.set(0);\n }\n\n public AtomicLong getCurrent() {\n return current;\n }\n\n public void setCurrent(AtomicLong current) {\n this.current = current;\n }\n\n public long getLastUpdate() {\n return lastUpdate;\n }\n\n public void setLastUpdate(long lastUpdate) {\n this.lastUpdate = lastUpdate;\n }\n\n }\n\n}","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3.3 最小活躍數","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"最小活躍數(Least Active)","attrs":{}},{"type":"text","text":"算法 將請求分發到連接數/請求數最少的候選服務器(目前處理請求最少的服務器)。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"特點:根據候選服務器當前的請求連接數,動態分配。","attrs":{}}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"場景:適用於對系統負載較爲敏感或請求連接時長相差較大的場景。","attrs":{}}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"由於每個請求的連接時長不一樣,如果採用簡單的輪循或隨機算法,都可能出現某些服務器當前連接數過大,而另一些服務器的連接過小的情況,這就造成了負載並非真正均衡。雖然,輪詢或算法都可以通過加權重屬性的方式進行負載調整,但加權方式難以應對動態變化。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"例如下圖中,(1, 3, 5) 請求會被髮送到服務器 1,但是 (1, 3) 很快就斷開連接,此時只有 (5) 請求連接服務器 1;(2, 4, 6) 請求被髮送到服務器 2,只有 (2) 的連接斷開。該系統繼續運行時,服務器 2 會承擔過大的負載。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/42/421e8bff0463932aba2b1ed14ee51087.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最小活躍數算法會記錄當前時刻,每個候選節點正在處理的連接數,然後選擇連接數最小的節點。該策略能夠動態、實時地反應服務器的當前狀況,較爲合理地將負責分配均勻,適用於對當前系統負載較爲敏感的場景。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"例如下圖中,服務器 1 當前連接數最小,那麼新到來的請求 6 就會被髮送到服務器 1 上。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/e0/e0303e8eaad543889d0ebd86a1821a6d.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"加權最小活躍數(Weighted Least Connection)","attrs":{}},{"type":"text","text":"在最小活躍數的基礎上,根據服務器的性能爲每臺服務器分配權重,再根據權重計算出每臺服務器能處理的連接數。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最小活躍數算法實現要點:活躍調用數越小,表明該服務節點處理能力越高,單位時間內可處理更多的請求,應優先將請求分發給該服務。在具體實現中,每個服務節點對應一個活躍數 active。初始情況下,所有服務提供者活躍數均爲 0。每收到一個請求,活躍數加 1,完成請求後則將活躍數減 1。在服務運行一段時間後,性能好的服務提供者處理請求的速度更快,因此活躍數下降的也越快,此時這樣的服務提供者能夠優先獲取到新的服務請求、這就是最小活躍數負載均衡算法的基本思想。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"【示例】最小活躍數算法實現","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以下實現基於 Dubbo 最小活躍數負載均衡算法做了些許改動。","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class LeastActiveLoadBalance extends BaseLoadBalance implements LoadBalance {\n\n private final Random random = new Random();\n\n @Override\n protected N doSelect(List nodes, String ip) {\n int length = nodes.size();\n // 最小的活躍數\n int leastActive = -1;\n // 具有相同“最小活躍數”的服務者提供者(以下用 Node 代稱)數量\n int leastCount = 0;\n // leastIndexs 用於記錄具有相同“最小活躍數”的 Node 在 nodes 列表中的下標信息\n int[] leastIndexs = new int[length];\n int totalWeight = 0;\n // 第一個最小活躍數的 Node 權重值,用於與其他具有相同最小活躍數的 Node 的權重進行對比,\n // 以檢測是否“所有具有相同最小活躍數的 Node 的權重”均相等\n int firstWeight = 0;\n boolean sameWeight = true;\n\n // 遍歷 nodes 列表\n for (int i = 0; i < length; i++) {\n N node = nodes.get(i);\n // 發現更小的活躍數,重新開始\n if (leastActive == -1 || node.getActive() < leastActive) {\n // 使用當前活躍數更新最小活躍數 leastActive\n leastActive = node.getActive();\n // 更新 leastCount 爲 1\n leastCount = 1;\n // 記錄當前下標值到 leastIndexs 中\n leastIndexs[0] = i;\n totalWeight = node.getWeight();\n firstWeight = node.getWeight();\n sameWeight = true;\n\n // 當前 Node 的活躍數 node.getActive() 與最小活躍數 leastActive 相同\n } else if (node.getActive() == leastActive) {\n // 在 leastIndexs 中記錄下當前 Node 在 nodes 集合中的下標\n leastIndexs[leastCount++] = i;\n // 累加權重\n totalWeight += node.getWeight();\n // 檢測當前 Node 的權重與 firstWeight 是否相等,\n // 不相等則將 sameWeight 置爲 false\n if (sameWeight && i > 0\n && node.getWeight() != firstWeight) {\n sameWeight = false;\n }\n }\n }\n\n // 當只有一個 Node 具有最小活躍數,此時直接返回該 Node 即可\n if (leastCount == 1) {\n return nodes.get(leastIndexs[0]);\n }\n\n // 有多個 Node 具有相同的最小活躍數,但它們之間的權重不同\n if (!sameWeight && totalWeight > 0) {\n // 隨機生成一個 [0, totalWeight) 之間的數字\n int offsetWeight = random.nextInt(totalWeight);\n // 循環讓隨機數減去具有最小活躍數的 Node 的權重值,\n // 當 offset 小於等於0時,返回相應的 Node\n for (int i = 0; i < leastCount; i++) {\n int leastIndex = leastIndexs[i];\n // 獲取權重值,並讓隨機數減去權重值\n offsetWeight -= nodes.get(leastIndex).getWeight();\n if (offsetWeight <= 0) {\n return nodes.get(leastIndex);\n }\n }\n }\n // 如果權重相同或權重爲0時,隨機返回一個 Node\n return nodes.get(leastIndexs[random.nextInt(leastCount)]);\n }\n\n}","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3.4 源地址哈希","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"源地址哈希(IP Hash)","attrs":{}},{"type":"text","text":"算法 根據請求源 IP,通過哈希計算得到一個數值,用該數值在候選服務器列表的進行取模運算,得到的結果便是選中的服務器。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以保證同一 IP 的客戶端的請求會轉發到同一臺服務器上,用來實現會話粘滯(Sticky Session)。","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"特點:保證特定用戶總是請求到相同的服務器,若服務器宕機,會話會丟失。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"【示例】源地址哈希算法實現示例","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class IpHashLoadBalance extends BaseLoadBalance implements LoadBalance {\n\n @Override\n protected N doSelect(List nodes, String ip) {\n if (StrUtil.isBlank(ip)) {\n ip = \"127.0.0.1\";\n }\n\n int length = nodes.size();\n int index = hash(ip) % length;\n return nodes.get(index);\n }\n\n public int hash(String text) {\n return HashUtil.fnvHash(text);\n }\n\n}","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3.5 一致性哈希","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一致性哈希(Consistent Hash)算法的目標是:相同的請求儘可能落到同一個服務器上。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一致性哈希 可以很好的解決 穩定性問題,可以將所有的 存儲節點 排列在 首尾相接 的 Hash 環上,每個 key 在計算 Hash 後會 順時針 找到 臨接 的 存儲節點 存放。而當有節點 加入 或 退出 時,僅影響該節點在 Hash環上順時針相鄰的後續節點。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/03/0395dd03dd099cb3e06e5a644c6a9b7c.png","alt":null,"title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1)相同的請求是指:一般在使用一致性哈希時,需要指定一個 key 用於 hash 計算,可能是:","attrs":{}}]},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用戶 ID","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"請求方 IP","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"請求服務名稱,參數列表構成的串","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2)儘可能是指:服務器可能發生上下線,少數服務器的變化不應該影響大多數的請求。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當某臺候選服務器宕機時,原本發往該服務器的請求,會基於虛擬節點,平攤到其它候選服務器,不會引起劇烈變動。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"優點:","attrs":{}},{"type":"text","text":"加入 和 刪除 節點隻影響 哈希環 中 順時針方向 的 相鄰的節點,對其他節點無影響。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"缺點:","attrs":{}},{"type":"text","text":"加減節點 會造成 哈希環 中部分數據 無法命中。當使用 少量節點 時,節點變化 將大範圍影響 哈希","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"環 中 數據映射,不適合 少量數據節點 的分佈式方案。普通 的 一致性哈希分區 在增減節點時需要 增加一倍 或 減去一半 節點才能保證 數據 和 負載的均衡。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"注意:","attrs":{}},{"type":"text","text":"因爲 一致性哈希分區 的這些缺點,一些分佈式系統採用 虛擬槽 對 一致性哈希 進行改進,比如 Dynamo 系統。","attrs":{}}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"【示例】一致性哈希算法示例","attrs":{}}]},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class ConsistentHashLoadBalance extends BaseLoadBalance implements LoadBalance {\n\n private final ConcurrentMap> selectors = new ConcurrentHashMap<>();\n\n @SuppressWarnings(\"unchecked\")\n @Override\n protected N doSelect(List nodes, String ip) {\n // 分片數,這裏設爲節點數的 4 倍\n Integer replicaNum = nodes.size() * 4;\n // 獲取 nodes 原始的 hashcode\n int identityHashCode = System.identityHashCode(nodes);\n\n // 如果 nodes 是一個新的 List 對象,意味着節點數量發生了變化\n // 此時 selector.identityHashCode != identityHashCode 條件成立\n ConsistentHashSelector selector = (ConsistentHashSelector) selectors.get(ip);\n if (selector == null || selector.identityHashCode != identityHashCode) {\n // 創建新的 ConsistentHashSelector\n selectors.put(ip, new ConsistentHashSelector<>(nodes, identityHashCode, replicaNum));\n selector = (ConsistentHashSelector) selectors.get(ip);\n }\n // 調用 ConsistentHashSelector 的 select 方法選擇 Node\n return selector.select(ip);\n }\n\n /**\n * 一致性哈希選擇器\n */\n private static final class ConsistentHashSelector {\n\n /**\n * 存儲虛擬節點\n */\n private final TreeMap virtualNodes;\n\n private final int identityHashCode;\n\n /**\n * 構造器\n *\n * @param nodes 節點列表\n * @param identityHashCode hashcode\n * @param replicaNum 分片數\n */\n ConsistentHashSelector(List nodes, int identityHashCode, Integer replicaNum) {\n this.virtualNodes = new TreeMap<>();\n this.identityHashCode = identityHashCode;\n // 獲取虛擬節點數,默認爲 100\n if (replicaNum == null) {\n replicaNum = 100;\n }\n for (N node : nodes) {\n for (int i = 0; i < replicaNum / 4; i++) {\n // 對 url 進行 md5 運算,得到一個長度爲16的字節數組\n byte[] digest = md5(node.getUrl());\n // 對 digest 部分字節進行 4 次 hash 運算,得到四個不同的 long 型正整數\n for (int j = 0; j < 4; j++) {\n // h = 0 時,取 digest 中下標爲 0 ~ 3 的4個字節進行位運算\n // h = 1 時,取 digest 中下標爲 4 ~ 7 的4個字節進行位運算\n // h = 2, h = 3 時過程同上\n long m = hash(digest, j);\n // 將 hash 到 node 的映射關係存儲到 virtualNodes 中,\n // virtualNodes 需要提供高效的查詢操作,因此選用 TreeMap 作爲存儲結構\n virtualNodes.put(m, node);\n }\n }\n }\n }\n\n public N select(String key) {\n // 對參數 key 進行 md5 運算\n byte[] digest = md5(key);\n // 取 digest 數組的前四個字節進行 hash 運算,再將 hash 值傳給 selectForKey 方法,\n // 尋找合適的 Node\n return selectForKey(hash(digest, 0));\n }\n\n private N selectForKey(long hash) {\n // 查找第一個大於或等於當前 hash 的節點\n Map.Entry entry = virtualNodes.ceilingEntry(hash);\n // 如果 hash 大於 Node 在哈希環上最大的位置,此時 entry = null,\n // 需要將 TreeMap 的頭節點賦值給 entry\n if (entry == null) {\n entry = virtualNodes.firstEntry();\n }\n // 返回 Node\n return entry.getValue();\n }\n\n }\n\n /**\n * 計算 hash 值\n */\n public static long hash(byte[] digest, int number) {\n return (((long) (digest[3 + number * 4] & 0xFF) << 24)\n | ((long) (digest[2 + number * 4] & 0xFF) << 16)\n | ((long) (digest[1 + number * 4] & 0xFF) << 8)\n | (digest[number * 4] & 0xFF))\n & 0xFFFFFFFFL;\n }\n\n /**\n * 計算 MD5 值\n */\n public static byte[] md5(String value) {\n MessageDigest md5;\n try {\n md5 = MessageDigest.getInstance(\"MD5\");\n } catch (NoSuchAlgorithmException e) {\n throw new IllegalStateException(e.getMessage(), e);\n }\n md5.reset();\n byte[] bytes = value.getBytes(StandardCharsets.UTF_8);\n md5.update(bytes);\n return md5.digest();\n }\n\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"以上示例基於 Dubbo 的一致性哈希負載均衡算法做了一些簡化。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"四、參考資料","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"1. ","attrs":{}},{"type":"link","attrs":{"href":"https://www.youtube.com/watch?reload=9&app=desktop&v=iqOTT7_7qXY","title":null,"type":null},"content":[{"type":"text","text":"Comparing Load Balancing Algorithms","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2. 《大型網站技術架構:核心原理與案例分析》","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"3. ","attrs":{}},{"type":"link","attrs":{"href":"https://www.cnblogs.com/itfly8/p/5043435.html","title":null,"type":null},"content":[{"type":"text","text":"大型網站架構系列:負載均衡詳解(1)","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"4. ","attrs":{}},{"type":"link","attrs":{"href":"https://zhuanlan.zhihu.com/p/32841479","title":null,"type":null},"content":[{"type":"text","text":"什麼是負載均衡","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"5. ","attrs":{}},{"type":"link","attrs":{"href":"https://avinetworks.com/what-is-load-balancing/","title":null,"type":null},"content":[{"type":"text","text":"What Is Load Balancing","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"6. ","attrs":{}},{"type":"link","attrs":{"href":"https://dubbo.apache.org/zh/docs/v2.7/dev/source/loadbalance/","title":null,"type":null},"content":[{"type":"text","text":"Dubbo 官方負載均衡算法說明","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"7. ","attrs":{}},{"type":"link","attrs":{"href":"https://segmentfault.com/a/1190000004492447","title":null,"type":null},"content":[{"type":"text","text":"負載均衡算法及手段","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"8. ","attrs":{}},{"type":"link","attrs":{"href":"https://segmentfault.com/a/1190000002578457","title":null,"type":null},"content":[{"type":"text","text":"利用 dns 解析來實現網站的負載均衡","attrs":{}}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作者:vivo互聯網團隊-Zhang Peng","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章