一、引子
從負載均衡策略可以瞭解到ribbon實現的各種負載均衡策略,當然也可以自己實現相應的算法。其中主要的接口IRule中有個定義如下:
public ILoadBalancer getLoadBalancer();
算法使用的server列表以及狀態信息都從它獲取,這裏,就從它開始。
二、接口
ILoadBalancer,從源碼的註釋中很容易看出,它定義負載均衡的操作,並對server進行存儲。接口如下:
public interface ILoadBalancer {
public void addServers(List<Server> newServers);
public Server chooseServer(Object key);
public void markServerDown(Server server);
public List<Server> getReachableServers();
public List<Server> getAllServers();
}
那麼ILoadBalancer的實現有哪些,參考下圖:
三、實現
-
首先看一下addServer如何實現
addServers(List newServers)的默認實現爲BaseLoadBalancer,其內部持有兩個list,如下:protected volatile List<Server> allServerList; protected volatile List<Server> upServerList;
allServerList代表所有的server,
upServerList代表已經起來的server。
addServers實現其實也比較簡單,即將server列表付給這兩個成員變量。 -
那麼如和區分server是否起來了呢?
其實實現是比較簡單的,BaseLoadBalancer內部啓動定時任務,每30秒執行一次ping任務,將ping通的server賦給upServerList。
這裏涉及到的ping就是我們之前說過的ping。 -
有了上面的實現,那麼剩餘的方法也就很容易了:
public List<Server> getReachableServers() { return Collections.unmodifiableList(upServerList); } public List<Server> getAllServers() { return Collections.unmodifiableList(allServerList); }
其實現僅僅是返回相應的成員變量。
-
可以看到BaseLoadBalancer有一個子類DynamicServerListLoadBalancer
從它的名字DynamicServerList可以看出它具有動態更新Server類別的功能,其實這個功能實現比較簡單,就是依賴ServerListUpdater實現的。
它獲取到server列表後,再委託ServerListFilter進行server過濾,最後設置到BaseLoadBalancer的兩個成員變量中:allServerList,upServerList。
到這裏,我們來張圖簡單總結一下server列表是如何動態更新的:
- ServerListUpdater通過定時任務發起server列表獲取。
- ServerList通過EurekaClient獲取server列表。
- EurekaClient發起http請求到EurekaServer,獲取server列表。
- ServerListUpdater獲取結果回調ILoadBalancer設置結果。
- ILoadBalancer通過ServerListFilter進行過濾。
- IPing定時ping,檢測server的活性。
這裏需要提一點,在狀態統計中LoadBalancerStats持有一個map,對應着zone和server列表,它的更新就來自於上面的流程,即DynamicServerListLoadBalancer最後獲取到server列表後,會對LoadBalancerStats的map進行更新。
-
ZoneAwareLoadBalancer
此類繼承自DynamicServerListLoadBalancer,是負載均衡器的最終實現,也是ribbon默認使用的類。它有如下功能:(翻譯自該類的註釋):- 一般來說,當選擇server時能夠避免某個不健康的zone。
- 衡量zone健康狀況的關鍵指標是平均活躍請求,即某個zone的平均活躍請求=這個zone中,還未完成的請求/存活的server
當某個狀況不好的zone慢慢的發生超時的時候(即大量的請求被卡住),這個指標是非常有效的。 - 此均衡器將會計算所有的存活的zone的狀況:
當任何某個zone平均活躍請求達到配置的閾值時,這個zone將會從被從存活列表中移除;
如果多個zone達到閾值,那麼具有平均server請求量最大的zone將會被移除; - 一旦狀況最壞的zone被移除,那麼將會按照zone具有的實例數量作爲概率來選擇某個zone。
- 選擇完zone,將會使用IRule來選擇一個server,默認的實現爲ZoneAvoidanceRule(由於此時只有一個zone,故ZoneAvoidanceRule退化爲AvailabilityFilteringRule)。
注:ZoneAwareLoadBalancer使用了ZoneAvoidanceRule來實現,對應如下三個方法:
- ZoneAvoidanceRule.createSnapshot
- ZoneAvoidanceRule.getAvailableZones
- ZoneAvoidanceRule.randomChooseZone
- 而這三個方法都是靜態方法,ZoneAvoidanceRule也使用了其中的方法來實現zone的排除和篩選,也就是說ZoneAwareLoadBalancer的zone過濾選擇和ZoneAvoidanceRule是一樣的但是ZoneAwareLoadBalancer採用了ZoneAvoidanceRule的靜態方法的方式實現,感覺有些ugly。
另外,ZoneAwareLoadBalancer持有一個map,爲每個zone都單獨創建了一個BaseLoadBalancer來進行過濾選定一個zone之後的server選擇。
-
最後,來看一下核心方法chooseServer是如何實現的,我們已經知道了最終負載均衡的實現是ZoneAwareLoadBalancer,而它也重寫了chooseServer,它的choose過程如下:
單個zone的choose過程說明:- 從LoadBalancerStats查詢存活的zone數量。
- 發現爲1,調用BaseLoadBalancer的chooseServer
- BaseLoadBalancer的實現很簡單,直接委託給具體的Rule來選擇
- 最終會使用ZoneAvoidanceRule,而此時zone的數量爲1,ZoneAvoidanceRule的zone過濾選擇功能不起作用,退化爲AvailabilityFilteringRule,即按照server斷路器打開或者併發量過大過濾掉一些server,再輪詢選擇一個。
多個zone的choose過程說明:
- 從LoadBalancerStats查詢存活的zone數量。
- 發現>1,則調用ZoneAvoidanceRule進行zone的過濾和挑選。
- 挑選好zone之後,跟 單個zone的choose過程一樣了,委託給BaseLoadBalancer來進行具體的server挑選。