上一講深刻的講解了Ribbon的初始化過程及Ribbon與Eureka的整合代碼,與Eureka整合的類就是DiscoveryEnableNIWSServerList
,同時在DynamicServerListLoadBalancer
中會調用PollingServerListUpdater
進行定時更新Eureka註冊表信息到BaseLoadBalancer
中,默認30s調度一次。java
咱們知道Ribbon主要是由3個組件組成的:算法
其中ILoadBalancer
前面咱們已經分析過了,接下來咱們一塊兒看看IRule
和IPing
中的具體實現。spring
目錄以下:服務器
原創不易,如若轉載 請標明來源!微信
博客地址:一枝花算不算浪漫
微信公衆號:壹枝花算不算浪漫併發
還記得咱們上一講說過,在Ribbon初始化過程當中,默認的IRule
爲ZoneAvoidanceRule
,這裏咱們能夠經過debug看看,從RibbonLoadBalancerClient.getServer()
一路往下跟,這裏直接看debug結果:app
而後咱們繼續跟ZoneAvoidanceRule.choose()
方法:負載均衡
public abstract class PredicateBasedRule extends ClientConfigEnabledRoundRobinRule { /** * Method that provides an instance of {@link AbstractServerPredicate} to be used by this class. * */ public abstract AbstractServerPredicate getPredicate(); /** * Get a server by calling {@link AbstractServerPredicate#chooseRandomlyAfterFiltering(java.util.List, Object)}. * The performance for this method is O(n) where n is number of servers to be filtered. */ @Override public Server choose(Object key) { ILoadBalancer lb = getLoadBalancer(); Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key); if (server.isPresent()) { return server.get(); } else { return null; } } }
這裏是調用的ZoneAvoidanceRule
的父類中的choose()
方法,首先是拿到對應的ILoadBalancer
,而後拿到對應的serverList數據,接着調用chooseRoundRobinAfterFiltering()
方法,繼續日後跟:dom
public abstract class AbstractServerPredicate implements Predicate<PredicateKey> { public Optional<Server> chooseRoundRobinAfterFiltering(List<Server> servers, Object loadBalancerKey) { List<Server> eligible = getEligibleServers(servers, loadBalancerKey); if (eligible.size() == 0) { return Optional.absent(); } return Optional.of(eligible.get(incrementAndGetModulo(eligible.size()))); } private int incrementAndGetModulo(int modulo) { for (;;) { int current = nextIndex.get(); int next = (current + 1) % modulo; if (nextIndex.compareAndSet(current, next) && current < modulo) return current; } } }
到了這裏能夠看到incrementAndGetModulo()
方法就是處理serverList輪詢的算法,這個和RoudRobinRule
中採用的是同樣的算法,這個算法你們能夠品一品,我這裏也會畫個圖來講明下:ide
看了圖=中的例子估計你們都會明白了,這個算法就是依次輪詢。這個算法寫的很精簡。
咱們上面知道,咱們按照輪詢的方式從serverList取到一個server後,那麼怎麼把以前原有的相似於:http://ServerA/sayHello/wangmeng
中的ServerA給替換成請求的ip數據呢?
接着咱們繼續看RibbonLoadBalancerClient.execute()
方法,這個裏面request.apply()
會作一個serverName的替換邏輯。
最後能夠一步步跟到RibbonLoadBalancerClient.reconstructURI()
,這個方法是把請求自帶的getURI方法給替換了,咱們最後查看context.reconstructURIWithServer()
方法,debug結果如圖,這個裏面會一步步把對應的請求url給拼接起來:
咱們知道 Ribbon還有一個重要的組件就是ping機制,經過上一講Ribbon的初始化咱們知道,默認的IPing實現類爲:NIWSDiscoveryPing
,咱們能夠查看其中的isAlive()
方法:
public class NIWSDiscoveryPing extends AbstractLoadBalancerPing { BaseLoadBalancer lb = null; public NIWSDiscoveryPing() { } public BaseLoadBalancer getLb() { return lb; } /** * Non IPing interface method - only set this if you care about the "newServers Feature" * @param lb */ public void setLb(BaseLoadBalancer lb) { this.lb = lb; } public boolean isAlive(Server server) { boolean isAlive = true; if (server!=null && server instanceof DiscoveryEnabledServer){ DiscoveryEnabledServer dServer = (DiscoveryEnabledServer)server; InstanceInfo instanceInfo = dServer.getInstanceInfo(); if (instanceInfo!=null){ InstanceStatus status = instanceInfo.getStatus(); if (status!=null){ isAlive = status.equals(InstanceStatus.UP); } } } return isAlive; } @Override public void initWithNiwsConfig( IClientConfig clientConfig) { } }
這裏就是獲取到DiscoveryEnabledServer
對應的註冊信息是否爲UP
狀態。那麼 既然有個ping的方法,確定會有方法進行調度的。
咱們能夠查看isAlive()
調用便可以找到調度的地方。
在BaseLoadBalancer
構造函數中會調用setupPingTask()
方法,進行調度:
protected int pingIntervalSeconds = 10; void setupPingTask() { if (canSkipPing()) { return; } if (lbTimer != null) { lbTimer.cancel(); } lbTimer = new ShutdownEnabledTimer("NFLoadBalancer-PingTimer-" + name, true); lbTimer.schedule(new PingTask(), 0, pingIntervalSeconds * 1000); forceQuickPing(); }
這裏pingIntervalSeconds
在BaseLoadBalancer
中定義的是10s,可是在initWithConfig()
方法中,經過傳入的時間覆蓋了本來的10s,這裏實際的默認時間是30s。以下代碼:
咱們也能夠經過debug來看看:
可能你們好奇爲何要單獨截圖來講這個事,實際上是由於網上好多博客講解都是錯的,都寫的是ping默認調度時間爲10s,想必他們都是沒有真正debug過吧。
仍是那句話,源碼出真知。
RoundRobinRule:系統內置的默認負載均衡規範,直接round robin輪詢,從一堆server list中,不斷的輪詢選擇出來一個server,每一個server平攤到的這個請求,基本上是平均的
這個算法,說白了是輪詢,就是一臺接着一臺去請求,是按照順序來的
AvailabilityFilteringRule:這個rule就是會考察服務器的可用性
若是3次鏈接失敗,就會等待30秒後再次訪問;若是不斷失敗,那麼等待時間會不斷變長
若是某個服務器的併發請求過高了,那麼會繞過去,再也不訪問
這裏先用round robin算法,輪詢依次選擇一臺server,若是判斷這個server是存活的可用的,若是這臺server是不能夠訪問的,那麼就用round robin算法再次選擇下一臺server,依次循環往復,10次。
WeightedResponseTimeRule:帶着權重的,每一個服務器能夠有權重,權重越高優先訪問,若是某個服務器響應時間比較長,那麼權重就會下降,減小訪問
ZoneAvoidanceRule:根據機房和服務器來進行負載均衡,說白了,就是機房的意思,看了源碼就是知道了,這個就是所謂的spring cloud ribbon環境中的默認的Rule
BestAvailableRule:忽略那些請求失敗的服務器,而後儘可能找併發比較低的服務器來請求
到了這裏 Ribbon相關的就結束了,對於Ribbon註冊表拉取及更新邏輯這裏也梳理下,這裏若是Ribbon保存的註冊表信息有宕機的狀況,這裏最多4分鐘才能感知到,因此spring cloud還有一個服務熔斷的機制,這個後面也會講到。
本文章首發自本人博客:https://www.cnblogs.com/wang-meng 和公衆號:壹枝花算不算浪漫,如若轉載請標明來源!
感興趣的小夥伴可關注我的公衆號:壹枝花算不算浪漫