【 58沈劍 架構師之路】TCP接入層的負載均衡、高可用、擴充套件性架構
一、web-server的負載均衡
網際網路架構中,web-server接入一般使用nginx來做反向代理,實施負載均衡。整個架構分三層:
-
上游呼叫層,一般是browser或者APP
-
中間反向代理層,nginx
-
下游真實接入叢集,web-server,常見web-server的有tomcat,apache
整個訪問過程為:
-
browser向daojia.com發起請求
-
DNS伺服器將daojia.com解析為外網IP(1.2.3.4)
-
browser通過外網IP(1.2.3.4)訪問nginx
-
nginx實施負載均衡策略,常見策略有輪詢,隨機,IP-hash等
-
nginx將請求轉發給內網IP(192.168.0.1)的web-server
由於http短連線,以及web應用無狀態的特性,理論上任何一個http請求落在任意一臺web-server都應該得到正常處理(如果必須落在一臺,說明架構不合理,不能水平擴充套件)。
問題來了,tcp是有狀態的連線,客戶端和服務端一旦建立連線,一個client發起的請求必須落在同一臺tcp-server上,此時如何做負載均衡,如何保證水平擴充套件呢?
二、單機法tcp-server
單個tcp-server顯然是可以保證請求一致性:
-
client向tcp.daojia.com發起tcp請求
-
DNS伺服器將tcp.daojia.com解析為外網IP(1.2.3.4)
-
client通過外網IP(1.2.3.4)向tcp-server發起請求
方案的缺點?
無法保證高可用。
三、叢集法tcp-server
通過搭建tcp-server叢集來保證高可用,客戶端來實現負載均衡:
-
client內配置有tcp1/tcp2/tcp3.daojia.com三個tcp-server的外網IP
-
客戶端通過“隨機”的方式選擇tcp-server,假設選擇到的是tcp1.daojia.com
-
通過DNS解析tcp1.daojia.com
-
通過外網IP連線真實的tcp-server
如何保證高可用呢?
如果client發現某個tcp-server連線不上,則選擇另一個。
潛在的缺點?
每次連線前,需要多實施一次DNS訪問:
-
難以預防DNS劫持
-
多一次DNS訪問意味著更長的連線時間,這個不足在手機端更為明顯
如何解決DNS的問題?
直接將IP配置在客戶端,可以解決上述兩個問題,很多公司也就是這麼做的(俗稱“IP直通車”)。
“IP直通車”有什麼新問題?
將IP寫死在客戶端,在客戶端實施負載均衡,擴充套件性很差:
-
如果原有IP發生變化,客戶端得不到實時通知
-
如果新增IP,即tcp-sever擴容,客戶端也得不到實時通知
-
如果負載均衡策略變化,需要升級客戶端
四、服務端實施負載均衡
只有將複雜的策略下沉到服務端,才能根本上解決擴充套件性的問題。
增加一個http介面,將客戶端的“IP配置”與“均衡策略”放到服務端是一個不錯的方案:
-
client每次訪問tcp-server前,先呼叫一個新增的get-tcp-ip介面,對於client而言,這個http介面只返回一個tcp-server的IP
-
這個http介面,實現的是原client的IP均衡策略
-
拿到tcp-server的IP後,和原來一樣向tcp-server發起TCP長連線
這樣的話,擴充套件性問題就解決了:
-
如果原有IP發生變化,只需要修改get-tcp-ip介面的配置
-
如果新增IP,也是修改get-tcp-ip介面的配置
-
如果負載均衡策略變化,需要升級客戶端
然而,新的問題又產生了,如果所有IP放在客戶端,當有一個IP掛掉的時候,client可以再換一個IP連線,保證可用性,而get-tcp-ip介面只是維護靜態的tcp-server叢集IP,對於這些IP對應的tcp-server是否可用,是完全不知情的,怎麼辦呢?
五、tcp-server狀態上報
get-tcp-ip介面怎麼知道tcp-server叢集中各臺伺服器是否可用呢,tcp-server主動上報是一個潛在方案,如果某一個tcp-server掛了,則會終止上報,對於停止上報狀態的tcp-server,get-tcp-ip介面,將不返回給client相應的tcp-server的外網IP。
該設計的存在的問題?
誠然,狀態上報解決了tcp-server高可用的問題,但這個設計犯了一個“反向依賴”的耦合小錯誤:使得tcp-server要依賴於一個與本身業務無關的web-server。
六、tcp-server狀態拉取
更優的方案是:web-server通過“拉”的方式獲取各個tcp-server的狀態,而不是tcp-server通過“推”的方式上報自己的狀態。
這樣的話,每個tcp-server都獨立與解耦,只需專注於資深的tcp業務功能即可。
高可用、負載均衡、擴充套件性等任務由get-tcp-ip的web-server專注來執行。
多說一句,將負載均衡實現在服務端,還有一個好處,可以實現異構tcp-server的負載均衡,以及過載保護:
-
靜態實施:web-server下的多個tcp-server的IP可以配置負載權重,根據tcp-server的機器配置分配負載(nginx也有類似的功能)
-
動態實施:web-server可以根據“拉”回來的tcp-server的狀態,動態分配負載,並在tcp-server效能極具下降時實施過載保護
七、總結
web-server如何實施負載均衡?
利用nginx反向代理來輪詢、隨機、ip-hash。
tcp-server怎麼快速保證請求一致性?
單機。
如何保證高可用?
客戶配置多個tcp-server的域名。
如何防止DNS劫持,以及加速?
IP直通車,客戶端配置多個tcp-server的IP。
如何保證擴充套件性?
服務端提供get-tcp-ip介面,向client屏遮蔽負載均衡策略,並實施便捷擴容。
如何保證高可用?
tcp-server“推”狀態給get-tcp-ip介面,
or
get-tcp-ip介面“拉”tcp-server狀態。