Ribbon 在實(shí)現(xiàn)客戶端負(fù)載均衡時母债,是通過Ribbo的ILoadBalancer接口實(shí)現(xiàn)的。
AbstractLoadBalancer
是ILoadBalancer接口的抽象實(shí)現(xiàn),定義了一個分組枚舉類ServerGroup
還實(shí)現(xiàn)了一個chooseServer()方法,其中key為null肾请,表示在選擇具體實(shí)例時忽略key的條件判斷
還定義了兩個抽象方法
getServerList(ServerGroup serverGroup):根據(jù)分組類型獲取不同的實(shí)例列表
getLoadBalancerStats():定義了獲取LoadBalancerStats對象的方法,
LoadBalancerStats對象被用來存儲負(fù)載均衡中各個服務(wù)實(shí)例當(dāng)前的屬性和統(tǒng)計(jì)信息更胖。
BaseLoadBalancer
是Ribbon負(fù)載均衡的基礎(chǔ)實(shí)現(xiàn)類,
定義并維護(hù)了兩個存儲服務(wù)實(shí)例Server對象的列表隔显。一個用于存儲所有服務(wù)實(shí)例的清單却妨,
一個用于存儲正常服務(wù)的實(shí)例清單
定義了各服務(wù)實(shí)例屬性和統(tǒng)計(jì)信息的LoadBalancerStats對象
定義了檢查服務(wù)實(shí)例是否正常服務(wù)的IPing對象,默認(rèn)為null括眠,構(gòu)造時注入
定義了檢查服務(wù)實(shí)例操作的執(zhí)行策略對象IPingStrategy彪标,在BaseLoadBalancer中
默認(rèn)使用了SerialPingStrategy,遍歷檢查
定義了負(fù)載均衡的處理規(guī)則IRule對象掷豺,從BaseLoadBalancer中
chooseServer(Object key)捞烟,實(shí)際將選擇任務(wù)委托給IRule.choose()方法,IRule默認(rèn)為
RoundRobinRule
啟動ping任務(wù):在BaseLoadBalancer的默認(rèn)構(gòu)造方法中当船,會直接啟動一個用于定時檢查Server是否健康的任務(wù)
DynamicServerListLoadBalancer
????? 對基礎(chǔ)負(fù)載均衡的擴(kuò)展题画。在該負(fù)載均衡中,實(shí)現(xiàn)了服務(wù)實(shí)例清單在運(yùn)行期的動態(tài)更新能力德频;
同時苍息,還具備了對服務(wù)實(shí)例清單的過濾功能。新增如下
ServerList serverListImpl
ServerList繼承結(jié)構(gòu)如下
上圖中有多個ServerList的實(shí)現(xiàn)類壹置,那么在DynamiceServerListLoadBalancer中的ServerList默認(rèn)配置到底使用了哪個具體實(shí)現(xiàn)竞思?
既然該負(fù)載均衡類中需要實(shí)現(xiàn)服務(wù)實(shí)例的動態(tài)更新,那么勢必需要Ribbon具備訪問Eureka來獲取服務(wù)實(shí)例的能力钞护,在包
org.springframework.cloud.netflix.ribbon.eureka下盖喷,可以找到配置類EurekaRibbonClientConfiguration,找到如下
這里創(chuàng)建的一個DomainExtractingServerList實(shí)例难咕,在這個類源碼中课梳,還定義類一個ServerList list. 同時對getInitialListOfServers()
和getUpdatedListOfServers()的具體實(shí)現(xiàn),其實(shí)委托給內(nèi)部定義的ServerList list對象
由構(gòu)造方法傳入的DiscoveryEnabledNIWSServerLIst實(shí)現(xiàn)的余佃。
在DiscoveryEnableNIWSServerList中
在obtainServersViaDiscovery()方法中
主要邏輯是惦界,依靠EurekaClient從服務(wù)注冊中心獲取到具體的服務(wù)實(shí)例InstanceInfo列表,vipAddress可以理解為邏輯上的服務(wù)名如USER-SERVICE
然后遍歷咙冗,找到UP的實(shí)例轉(zhuǎn)換成DiscoveryEnabledServer對象 沾歪,返回
返回的結(jié)果List到了DomainExtractingServerList類中,將繼續(xù)通過setZones()方法進(jìn)行處理
ServerListUpdater
DynamicServerListLoadBalancer類中屬性ServerListUpdater中
主要是對ServerList的更新雾消,
而ServerListUpdater的實(shí)現(xiàn)類不多灾搏,如下
?? PollingServerListUpdater:動態(tài)服務(wù)列表更新的默認(rèn)策略挫望,也就是DynamicServerListLoadBalancer中默認(rèn)實(shí)現(xiàn),
它通過定時任務(wù)實(shí)現(xiàn)更新
EurekaNotificationServerListUpdater 需要利用Eureka的事件監(jiān)聽器來驅(qū)動服務(wù)列表的更新操作
ServerListFilter
回到updateAction.doUpdate()方法狂窑,在DynamicServerListLoadBalancer中媳板,調(diào)用updateListOfServers()方法
調(diào)用之前提到的ServerList.getUpdatedListOfServers(),獲取到從Eureka Server中獲取服務(wù)可用實(shí)例的列表泉哈。
通過ServerListFilter filter過濾蛉幸,繼承關(guān)系如下
AbstractServerListFilter:定義類過濾時需要的一個重要對象LoadBalancerStats,該對象存儲了一些屬性和統(tǒng)計(jì)信息等
?????? ZoneAffinityServerListFilter:該過濾器基于“區(qū)域感知(Zone Affinity)”的方式實(shí)現(xiàn)服務(wù)實(shí)例的過濾丛晦,源碼
過濾后奕纫,通過shouldEnableZoneAffinity()方法來判斷是否啟用“區(qū)域感知”功能。
使用LoadBalancerStats.getZoneSnapshot()獲取過濾后同區(qū)域?qū)嵗幕A(chǔ)指標(biāo)(包含實(shí)例數(shù)量烫沙,斷路器斷開數(shù)匹层,活動請求數(shù),實(shí)例平均負(fù)載等)
根據(jù)一系列的算法求出下面的幾個評價值并與設(shè)置的閾值進(jìn)行比較锌蓄,若有一個條件符合升筏,就不啟用“區(qū)域感知”。
可以實(shí)現(xiàn)當(dāng)集群出現(xiàn)區(qū)域故障時瘸爽,依然可以依靠其他區(qū)域的實(shí)例進(jìn)行正常服務(wù)的高可用保障您访。
blackOutServerPercentage:故障實(shí)例百分比(斷路器斷開數(shù)/實(shí)例數(shù))>=0.8
?????? activeRequestsPerServer:實(shí)例平均負(fù)載>=0.6
?????? availableSevers: 可用實(shí)例數(shù)(實(shí)例數(shù) - 斷路器斷開數(shù))<2
DefaultNIWSServerListFilter 完全繼承ZoneAffinityServerListFilter,是默認(rèn)NIWS(Netflix Internal Web Server)過濾器
ServerListSubSetFilter:
ZonePreferenceServerListFilter: Spring Cloud整合時新增的過濾器剪决。若使用Spring Cloud整合Eureka和Ribbon時默認(rèn)使用該過濾器
根據(jù)zone過濾出實(shí)例
ZoneAwareLoadBalancer
?是對DynamicServerListLoadBalancer的擴(kuò)展洋只。DynamicServerListLoadBalancer中,沒重寫chooseServer()方法昼捍,所有它
依舊采用BaseLoadBalancer中的算法识虚。使用RoundRobinRule規(guī)則,以線性輪詢的方式來選擇調(diào)用的服務(wù)實(shí)例妒茬,該算法實(shí)現(xiàn)
簡單并沒有區(qū)域(Zone)的概念担锤,所以它會把所有實(shí)例視為一個Zone下的節(jié)點(diǎn)來看待,這樣就會周期性地產(chǎn)生跨區(qū)域(Zone)
訪問的情況乍钻,由于跨區(qū)域會產(chǎn)生更高的延遲肛循,這些實(shí)例主要以防止區(qū)域性故障實(shí)現(xiàn)高可用為目的而不能作為常規(guī)訪問的實(shí)例,
所以在多區(qū)域部署的情況下會有一定的性能問題银择,而該負(fù)載均衡則可以避免這樣的問題多糠。
在ZoneAwareLoadBalancer中,它沒有重寫setServersList浩考,說明實(shí)現(xiàn)服務(wù)實(shí)例清單的更新主邏輯沒有修改夹孔。但是發(fā)現(xiàn)它重寫了setServerListForZones(Map
List>zoneServersMap)。
在DynamicServerListLoadBalancer中查找這個方法
setServerListForZones()在
setServersList()方法的最后被調(diào)用,在父類DynamicServerListLoadBalancer中的作用是根據(jù)按區(qū)域Zone分組的實(shí)例列表搭伤,為LoadBalancerStats對象創(chuàng)建ZoneStats并放入Map
zoneStatsMap中只怎,每一個Zone對應(yīng)一個
ZoneStats,它用于存儲每個Zone的一些狀態(tài)和統(tǒng)計(jì)信息。
?? 在ZoneAwareLoadBalancer中setServerListForZones()如下
?可以看到怜俐,在該實(shí)現(xiàn)中創(chuàng)建了一個ConcurentHashMap()類型的balancers對象身堡,它將用來存儲每個Zone區(qū)域?qū)?yīng)的負(fù)載均衡器。
?其中g(shù)etLoadBalancer創(chuàng)建如下
?在創(chuàng)建負(fù)載均衡器的時候會創(chuàng)建它的規(guī)則(如果當(dāng)前實(shí)現(xiàn)中沒有IRule實(shí)例拍鲤,就創(chuàng)建一個AvailablityFilteringRule:否則克绿选)
創(chuàng)建完負(fù)載均衡器后有調(diào)用setServersList()為其設(shè)置對應(yīng)Zone區(qū)域的實(shí)例清單。
?? 第二個循環(huán)則對Zone區(qū)域中實(shí)例清單的檢查季稳,看看是否有Zone區(qū)域下已經(jīng)沒有實(shí)例了
現(xiàn)在看一下ZoneAwareLoadBalancer中chooseServer()方法
當(dāng)zone區(qū)域大于1時
?? ①調(diào)用ZoneAvoidanceRule中的靜態(tài)方法createSnapshot(lbStats)擅这,為當(dāng)前負(fù)載均衡器中所有的Zone區(qū)域創(chuàng)建快照,
保存在Map zoneSnapshot中
②調(diào)用ZoneAvoidancerRule中ZoneAvoidanceRule.getAvailableZones(zoneSnapshot,
triggeringLoad.get(), triggeringBlackoutPercentage.get());
來獲取可用的Zone區(qū)域集合绞幌,通過Zone區(qū)域快照中的統(tǒng)計(jì)數(shù)據(jù)來實(shí)現(xiàn)可用區(qū)
的挑選。