大家好,本文我將繼續來剖析SpringCloud中負載均衡元件Ribbon的原始碼。本來我是打算接著OpenFeign動態代理生成文章直接講Feign是如何整合Ribbon的,但是文章寫了一半發現,如果不把Ribbon好好講清楚,那麼有些Ribbon的細節理解起來就很困難,所以我還是打算單獨寫一篇文章來剖析Ribbon的原始碼,這樣在講Feign整合Ribbon的時候,我就不再贅述這些細節了。好了,話不多說,直接進入主題。
一、Ribbon的核心元件
1、Server
這是個很簡單的東西,就是服務例項資料的封裝,裡面封裝了服務例項的ip和埠之類的,一個服務有很多臺機器,那就有很多個Server物件。
2、ServerList
public interface ServerList<T extends Server> { public List<T> getInitialListOfServers(); /** * Return updated list of servers. This is called say every 30 secs * (configurable) by the Loadbalancer's Ping cycle * */ public List<T> getUpdatedListOfServers(); }
ServerList是個介面,泛型是Server,提供了兩個方法,都是獲取服務例項列表的,這兩個方法其實在很多實現類中實現是一樣的,沒什麼區別。這個介面很重要,因為這個介面就是Ribbon獲取服務資料的來源介面,Ribbon進行負載均衡的服務列表就是通過這個介面來的,那麼可以想一想是不是隻要實現這個介面就可以給Ribbon提供服務資料了?事實的確如此,在SpringCloud中,eureka、nacos等註冊中心都實現了這個介面,都將註冊中心的服務例項資料提供給Ribbon,供Ribbon來進行負載均衡。
3、ServerListUpdater
通過名字也可以知道,是用來更新服務登錄檔的資料,他有唯一的實現,就是PollingServerListUpdater,這個類有一個核心的方法,就是start,我們來看一下start的實現。
@Override public synchronized void start(final UpdateAction updateAction) { if (isActive.compareAndSet(false, true)) { final Runnable wrapperRunnable = new Runnable() { @Override public void run() { if (!isActive.get()) { if (scheduledFuture != null) { scheduledFuture.cancel(true); } return; } try { updateAction.doUpdate(); lastUpdated = System.currentTimeMillis(); } catch (Exception e) { logger.warn("Failed one update cycle", e); } } }; scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay( wrapperRunnable, initialDelayMs, refreshIntervalMs, TimeUnit.MILLISECONDS ); } else { logger.info("Already active, no-op"); } }
通過這段方法我們可以看出,首先通過isActive.compareAndSet(false, true)來保證這個方法只會被呼叫一下,然後封裝了一個Runnable,這個Runnable幹了一件核心的事,就是呼叫傳入的updateAction的doUpdate方法,然後將Runnable扔到了帶定時排程功能的執行緒池,經過initialDelayMs(預設1s)時間後,會呼叫一次,之後都是每隔refreshIntervalMs(預設30s)呼叫一次Runnable的run方法,也就是呼叫updateAction的doUpdate方法。
所以這個類的核心作用就是每隔30s會呼叫一次傳入的updateAction的doUpdate方法的實現,記住這個結論。
4、IRule
public interface IRule{ /* * choose one alive server from lb.allServers or * lb.upServers according to key * * @return choosen Server object. NULL is returned if none * server is available */ public Server choose(Object key); public void setLoadBalancer(ILoadBalancer lb); public ILoadBalancer getLoadBalancer(); }
IRule是負責負載均衡的演算法的,也就是真正實現負載均衡獲取一個服務例項就是這個介面的實現。比如說實現類RandomRule,就是從一堆服務例項中隨機選取一個服務例項。
5、IClientConfig
就是一個配置介面,有個預設的實現DefaultClientConfigImpl,通過這個可以獲取到一些配置Ribbon的一些配置。
6、ILoadBalancer
public interface ILoadBalancer { public void addServers(List<Server> newServers); public Server chooseServer(Object key); public void markServerDown(Server server); @Deprecated public List<Server> getServerList(boolean availableOnly); public List<Server> getReachableServers(); public List<Server> getAllServers(); }
這個介面的作用,對外主要提供了獲取服務例項列表和選擇服務例項的功能。雖然對外主要提供獲取服務的功能,但是在實現的時候,主要是用來協調上面提到的各個核心元件的,使得他們能夠協調工作,從而實現對外提供獲取服務例項的功能。
這個介面的實現有好幾個實現類,但是我講兩個比較重要的。
BaseLoadBalancer
public class BaseLoadBalancer extends AbstractLoadBalancer implements PrimeConnections.PrimeConnectionListener, IClientConfigAware { private final static IRule DEFAULT_RULE = new RoundRobinRule(); protected IRule rule = DEFAULT_RULE; private IClientConfig config; protected volatile List<Server> allServerList = Collections .synchronizedList(new ArrayList<Server>()); protected volatile List<Server> upServerList = Collections .synchronizedList(new ArrayList<Server>()); public BaseLoadBalancer(String name, IRule rule, LoadBalancerStats stats, IPing ping, IPingStrategy pingStrategy) { logger.debug("LoadBalancer [{}]: initialized", name); this.name = name; this.ping = ping; this.pingStrategy = pingStrategy; setRule(rule); setupPingTask(); lbStats = stats; init(); } public BaseLoadBalancer(IClientConfig config) { initWithNiwsConfig(config); } public BaseLoadBalancer(IClientConfig config, IRule rule, IPing ping) { initWithConfig(config, rule, ping, createLoadBalancerStatsFromConfig(config)); } void initWithConfig(IClientConfig clientConfig, IRule rule, IPing ping, LoadBalancerStats stats) { this.config = clientConfig; String clientName = clientConfig.getClientName(); this.name = clientName; int pingIntervalTime = Integer.parseInt("" + clientConfig.getProperty( CommonClientConfigKey.NFLoadBalancerPingInterval, Integer.parseInt("30"))); int maxTotalPingTime = Integer.parseInt("" + clientConfig.getProperty( CommonClientConfigKey.NFLoadBalancerMaxTotalPingTime, Integer.parseInt("2"))); setPingInterval(pingIntervalTime); setMaxTotalPingTime(maxTotalPingTime); // cross associate with each other // i.e. Rule,Ping meet your container LB // LB, these are your Ping and Rule guys ... setRule(rule); setPing(ping); setLoadBalancerStats(stats); rule.setLoadBalancer(this); if (ping instanceof AbstractLoadBalancerPing) { ((AbstractLoadBalancerPing) ping).setLoadBalancer(this); } logger.info("Client: {} instantiated a LoadBalancer: {}", name, this); boolean enablePrimeConnections = clientConfig.get( CommonClientConfigKey.EnablePrimeConnections, DefaultClientConfigImpl.DEFAULT_ENABLE_PRIME_CONNECTIONS); if (enablePrimeConnections) { this.setEnablePrimingConnections(true); PrimeConnections primeConnections = new PrimeConnections( this.getName(), clientConfig); this.setPrimeConnections(primeConnections); } init(); } public void setRule(IRule rule) { if (rule != null) { this.rule = rule; } else { /* default rule */ this.rule = new RoundRobinRule(); } if (this.rule.getLoadBalancer() != this) { this.rule.setLoadBalancer(this); } } public Server chooseServer(Object key) { if (counter == null) { counter = createCounter(); } counter.increment(); if (rule == null) { return null; } else { try { return rule.choose(key); } catch (Exception e) { logger.warn("LoadBalancer [{}]: Error choosing server for key {}", name, key, e); return null; } } } }
核心屬性
allServerList:快取了所有的服務例項資料
upServerList:快取了能夠使用的服務例項資料。
rule:負載均衡演算法元件,預設是RoundRobinRule
核心方法
setRule:這個方法是設定負載均衡演算法的,並將當前這個ILoadBalancer物件設定給IRule,從這可以得出一個結論,IRule進行負載均衡的服務例項列表是通過ILoadBalancer獲取的,也就是 IRule 和 ILoadBalancer相互引用。setRule(rule)一般是在構造物件的時候會呼叫。
chooseServer:就是選擇一個服務例項,是委派給IRule的choose方法來實現服務例項的選擇。
BaseLoadBalancer這個實現類總體來說,已經實現了ILoadBalancer的功能的,所以這個已經基本滿足使用了。
說完BaseLoadBalancer這個實現類,接下來說一下DynamicServerListLoadBalancer實現類。DynamicServerListLoadBalancer繼承自BaseLoadBalancer,DynamicServerListLoadBalancer主要是對BaseLoadBalancer功能進行擴充套件。
DynamicServerListLoadBalancer
public class DynamicServerListLoadBalancer<T extends Server> extends BaseLoadBalancer { private static final Logger LOGGER = LoggerFactory.getLogger(DynamicServerListLoadBalancer.class); volatile ServerList<T> serverListImpl; volatile ServerListFilter<T> filter; protected final ServerListUpdater.UpdateAction updateAction = new ServerListUpdater.UpdateAction() { @Override public void doUpdate() { updateListOfServers(); } }; protected volatile ServerListUpdater serverListUpdater; public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping, ServerList<T> serverList, ServerListFilter<T> filter, ServerListUpdater serverListUpdater) { super(clientConfig, rule, ping); this.serverListImpl = serverList; this.filter = filter; this.serverListUpdater = serverListUpdater; if (filter instanceof AbstractServerListFilter) { ((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats()); } restOfInit(clientConfig); } @Override public void setServersList(List lsrv) { super.setServersList(lsrv); List<T> serverList = (List<T>) lsrv; Map<String, List<Server>> serversInZones = new HashMap<String, List<Server>>(); for (Server server : serverList) { // make sure ServerStats is created to avoid creating them on hot // path getLoadBalancerStats().getSingleServerStat(server); String zone = server.getZone(); if (zone != null) { zone = zone.toLowerCase(); List<Server> servers = serversInZones.get(zone); if (servers == null) { servers = new ArrayList<Server>(); serversInZones.put(zone, servers); } servers.add(server); } } setServerListForZones(serversInZones); } protected void setServerListForZones( Map<String, List<Server>> zoneServersMap) { LOGGER.debug("Setting server list for zones: {}", zoneServersMap); getLoadBalancerStats().updateZoneServerMapping(zoneServersMap); } @VisibleForTesting public void updateListOfServers() { List<T> servers = new ArrayList<T>(); if (serverListImpl != null) { servers = serverListImpl.getUpdatedListOfServers(); LOGGER.debug("List of Servers for {} obtained from Discovery client: {}", getIdentifier(), servers); if (filter != null) { servers = filter.getFilteredListOfServers(servers); LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}", getIdentifier(), servers); } } updateAllServerList(servers); } /** * Update the AllServer list in the LoadBalancer if necessary and enabled * * @param ls */ protected void updateAllServerList(List<T> ls) { // other threads might be doing this - in which case, we pass if (serverListUpdateInProgress.compareAndSet(false, true)) { try { for (T s : ls) { s.setAlive(true); // set so that clients can start using these // servers right away instead // of having to wait out the ping cycle. } setServersList(ls); super.forceQuickPing(); } finally { serverListUpdateInProgress.set(false); } } } }
成員變數
serverListImpl:上面說過,通過這個介面獲取服務列表
filter:起到過濾的作用,一般不care
updateAction:是個匿名內部類,實現了doUpdate方法,會呼叫updateListOfServers方法
serverListUpdater:上面說到過,預設就是唯一的實現類PollingServerListUpdater,也就是每個30s就會呼叫傳入的updateAction的doUpdate方法。
這不是巧了麼,serverListUpdater的start方法需要一個updateAction,剛剛好成員變數有個updateAction的匿名內部類的實現,所以serverListUpdater的start方法傳入的updateAction的實現其實就是這個匿名內部類。
那麼哪裡呼叫了serverListUpdater的start方法傳入了updateAction呢?是在構造的時候呼叫的,具體的呼叫鏈路是呼叫 restOfInit -> enableAndInitLearnNewServersFeature(),這裡就不貼原始碼了
所以,其實DynamicServerListLoadBalancer在構造完成之後,預設每隔30s中,就會呼叫updateAction的匿名內部類的doUpdate方法,從而會呼叫updateListOfServers。所以我們來看一看 updateListOfServers 方法幹了什麼。
public void updateListOfServers() { List<T> servers = new ArrayList<T>(); if (serverListImpl != null) { servers = serverListImpl.getUpdatedListOfServers(); LOGGER.debug("List of Servers for {} obtained from Discovery client: {}", getIdentifier(), servers); if (filter != null) { servers = filter.getFilteredListOfServers(servers); LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}", getIdentifier(), servers); } } updateAllServerList(servers); }
這個方法實現很簡單,就是通過呼叫 ServerList 的getUpdatedListOfServers獲取到一批服務例項資料,然後過濾一下,最後呼叫updateAllServerList方法,進入updateAllServerList方法。
protected void updateAllServerList(List<T> ls) { // other threads might be doing this - in which case, we pass if (serverListUpdateInProgress.compareAndSet(false, true)) { try { for (T s : ls) { s.setAlive(true); // set so that clients can start using these // servers right away instead // of having to wait out the ping cycle. } setServersList(ls); super.forceQuickPing(); } finally { serverListUpdateInProgress.set(false); } } }
其實很簡單,就是呼叫每個服務例項的setAlive方法,將isAliveFlag設定成true,然後呼叫setServersList。setServersList這個方法的主要作用是將服務例項更新到內部的快取中,也就是上面提到的allServerList和upServerList,這裡就不貼原始碼了。
其實分析完updateListOfServers方法之後,再結合上面原始碼的分析,我們可以清楚的得出一個結論,那就是預設每隔30s都會重新通過ServerList元件獲取到服務例項資料,然後更新到BaseLoadBalancer快取中,IRule的負載均衡所需的服務例項資料,就是這個內部快取。
從DynamicServerListLoadBalancer的命名也可以看出,他相對於父類BaseLoadBalancer而言,提供了動態更新內部服務例項列表的功能。
為了便於大家記憶,我畫一張圖來描述這些元件的關係以及是如何運作的。
說完一些核心的元件,以及他們跟ILoadBalancer的關係之後,接下來就來分析一下,ILoadBalancer是在ribbon中是如何使用的。
8、AbstractLoadBalancerAwareClient
ILoadBalancer是一個可以獲取到服務例項資料的元件,那麼服務例項跟什麼有關,那麼肯定是跟請求有關,所以在Ribbon中有這麼一個抽象類,AbstractLoadBalancerAwareClient,這個是用來執行請求的,我們來看一下這個類的構造。
public AbstractLoadBalancerAwareClient(ILoadBalancer lb) { super(lb); } /** * Delegate to {@link #initWithNiwsConfig(IClientConfig)} * @param clientConfig */ public AbstractLoadBalancerAwareClient(ILoadBalancer lb, IClientConfig clientConfig) { super(lb, clientConfig); }
通過上面可以看出,在構造的時候需要傳入一個ILoadBalancer。
AbstractLoadBalancerAwareClient中有一個方法executeWithLoadBalancer,這個是用來執行傳入的請求,以負載均衡的方式。
public T executeWithLoadBalancer(final S request, final IClientConfig requestConfig) throws ClientException { LoadBalancerCommand<T> command = buildLoadBalancerCommand(request, requestConfig); try { return command.submit( new ServerOperation<T>() { @Override public Observable<T> call(Server server) { URI finalUri = reconstructURIWithServer(server, request.getUri()); S requestForServer = (S) request.replaceUri(finalUri); try { return Observable.just(AbstractLoadBalancerAwareClient.this.execute(requestForServer, requestConfig)); } catch (Exception e) { return Observable.error(e); } } }) .toBlocking() .single(); } catch (Exception e) { Throwable t = e.getCause(); if (t instanceof ClientException) { throw (ClientException) t; } else { throw new ClientException(e); } } }
這個方法構建了一個LoadBalancerCommand,隨後呼叫了submit方法,傳入了一個匿名內部類,這個匿名內部類中有這麼一行程式碼很重要。
URI finalUri = reconstructURIWithServer(server, request.getUri());
這行程式碼是根據給定的一個Server重構了URI,這是什麼意思呢?舉個例子,在OpenFeign那一篇文章我說過,會根據服務名拼接出類似http://ServerA的地址,那時是沒有伺服器的ip地址的,只有服務名,假設請求的地址是http://ServerA/api/sayHello,那麼reconstructURIWithServer乾的一件事就是將ServerA服務名替換成真正的服務所在的機器的ip和埠,假設ServerA所在的一臺機器(Server裡面封裝了某臺機器的ip和埠)是192.168.1.101:8088,那麼重構後的地址就變成http://192.168.1.101:8088/api/sayHello,這樣就能傳送http請求到ServerA服務所對應的一臺伺服器了。
之後根據新的地址,呼叫這個類中的execute方法來執行請求,execute方法是個抽象方法,也就是交給子類實現,子類就可以通過實現這個方法,來傳送http請求,實現rpc呼叫。
那麼這臺Server是從獲取的呢?其實猜猜也知道,肯定是通過ILoadBalancer獲取的,因為submit方法比較長,這裡我直接貼出submit方法中核心的一部分程式碼
Observable<T> o = (server == null ? selectServer() : Observable.just(server))
就是通過selectServer來選擇一個Server的,selectServer我就不翻原始碼了,其實最終還是呼叫ILoadBalancer的方法chooseServer方法來獲取一個服務,之後就會呼叫上面的說的匿名內部類的方法,重構URI,然後再交由子類的execut方法來實現傳送http請求。
所以,通過對AbstractLoadBalancerAwareClient的executeWithLoadBalancer方法,我們可以知道,這個抽象類的主要作用就是通過負載均衡演算法,找到一個合適的Server,然後將你傳入的請求路徑http://ServerA/api/sayHello重新構建成類似http://192.168.1.101:8088/api/sayHello這樣,之後呼叫子類實現的execut方法,來傳送http請求,就是這麼簡單。到這裡其實Ribbon核心元件和執行原理我就已經說的差不多了,再來畫一張圖總結一下
二、SpringCloud中使用的核心元件的實現都有哪些
說完了Ribbon的一些核心元件和執行原理之後,我們再來看一下在SpringCloud環境下,這些元件到底是用的哪些實現,畢竟有寫時介面,有的是抽象類。
Ribbon的自動裝配類:RibbonAutoConfiguration,我拎出了核心的原始碼
@Configuration @RibbonClients public class RibbonAutoConfiguration { @Autowired(required = false) private List<RibbonClientSpecification> configurations = new ArrayList<>(); @Bean public SpringClientFactory springClientFactory() { SpringClientFactory factory = new SpringClientFactory(); factory.setConfigurations(this.configurations); return factory; } }
RibbonAutoConfiguration配置類上有個@RibbonClients註解,接下來講解一下這個註解的作用
@Import(RibbonClientConfigurationRegistrar.class) public @interface RibbonClients { RibbonClient[] value() default {}; Class<?>[] defaultConfiguration() default {}; }
看過我寫的OpenFeign的文章小夥伴肯定知道,要使用Feign,得需要使用@EnableFeignClients,@EnableFeignClients的作用可以掃描指定包路徑下的@FeignClient註解,也可以宣告配置類;同樣RibbonClients的作用也是可以宣告配置類,同樣也使用了@Import註解註解來實現的,RibbonClientConfigurationRegistrar這個配置類的作用就是往spring容器中注入每個服務的Ribbon元件(@RibbonClient裡面可以宣告每個服務對應的配置)的配置類和預設配置類,將配置類封裝為RibbonClientSpecification注入到spring容器中,其實就跟@FeignClient註解宣告配置的作用是一樣的。
RibbonAutoConfiguration的主要作用就是注入了一堆RibbonClientSpecification,就是每個服務對應的配置類,然後宣告瞭SpringClientFactory這個bean,將配置類放入到裡面。
SpringClientFactory是不是感覺跟OpenFeign中的FeignContext很像,其實兩個的作用是一樣的,SpringClientFactory也繼承了NamedContextFactory,實現了配置隔離,同時也在構造方法中傳入了每個容器預設的配置類RibbonClientConfiguration。至於什麼是配置隔離,我在OpenFeign那篇文章說過,不清楚的小夥伴可以後臺回覆feign01即可獲得文章連結。
配置優先順序問題
這裡我說一下在OpenFeign裡沒仔細說的配置優先順序的事情,因為有這麼多配置類,都可以在配置類中宣告物件,那麼到底使用哪個配置類宣告的物件呢。
優先順序最高的是springboot啟動的時候的容器,因為這個容器是每個服務的容器的父容器,而在配置類宣告bean的時候,都有@ConditionalOnMissingBean註解,一旦父容器有這個bean,那麼子容器就不會初始化。
優先順序第二高的是每個客戶端宣告的配置類,也就是通過@FeignClient和@RibbonClient的configuration屬性宣告的配置類
優先順序第三高的是@EnableFeignClients和@RibbonClients註解中configuration屬性宣告的配置類
優先順序最低的就是FeignContext和SpringClientFactory構造時傳入的配置類
至於優先順序怎麼來的,其實是在NamedContextFactory中createContext方法中構建AnnotationConfigApplicationContext時按照配置的優先順序一個一個傳進去的。
RibbonClientConfiguration提供的預設的bean
接下來我們看一下RibbonClientConfiguration都提供了哪些預設的bean
@Bean @ConditionalOnMissingBean public IClientConfig ribbonClientConfig() { DefaultClientConfigImpl config = new DefaultClientConfigImpl(); config.loadProperties(this.name); config.set(CommonClientConfigKey.ConnectTimeout, DEFAULT_CONNECT_TIMEOUT); config.set(CommonClientConfigKey.ReadTimeout, DEFAULT_READ_TIMEOUT); config.set(CommonClientConfigKey.GZipPayload, DEFAULT_GZIP_PAYLOAD); return config; }
配置類對應的bean,這裡設定了ConnectTimeout和ReadTimeout都是1s中。
@Bean @ConditionalOnMissingBean public IRule ribbonRule(IClientConfig config) { if (this.propertiesFactory.isSet(IRule.class, name)) { return this.propertiesFactory.get(IRule.class, config, name); } ZoneAvoidanceRule rule = new ZoneAvoidanceRule(); rule.initWithNiwsConfig(config); return rule; }
IRule,預設是ZoneAvoidanceRule,這個Rule帶有過濾的功能,過濾哪些不可用的分割槽的服務(這個過濾可以不用care),過濾成功之後,繼續採用線性輪詢的方式從過濾結果中選擇一個出來。至於這個propertiesFactory,可以不用管,這個是預設讀配置檔案的中的配置,一般不設定,後面看到都不用care。
@Bean @ConditionalOnMissingBean @SuppressWarnings("unchecked") public ServerList<Server> ribbonServerList(IClientConfig config) { if (this.propertiesFactory.isSet(ServerList.class, name)) { return this.propertiesFactory.get(ServerList.class, config, name); } ConfigurationBasedServerList serverList = new ConfigurationBasedServerList(); serverList.initWithNiwsConfig(config); return serverList; }
預設是ConfigurationBasedServerList,也就是基於配置來提供服務例項列表。但是在SpringCloud環境中,這是不可能的,因為服務資訊是在註冊中心,所以應該是服務註冊中心對應實現的,比如Nacos的實現NacosServerList,這裡我貼出NacosServerList的bean的宣告,在配置類NacosRibbonClientConfiguration中
@Bean @ConditionalOnMissingBean public ServerList<?> ribbonServerList(IClientConfig config, NacosDiscoveryProperties nacosDiscoveryProperties) { NacosServerList serverList = new NacosServerList(nacosDiscoveryProperties); serverList.initWithNiwsConfig(config); return serverList; }
至於為什麼容器選擇NacosServerList而不是ConfigurationBasedServerList,主要是因為NacosRibbonClientConfiguration這個配置類是通過@RibbonClients匯入的,也就是比SpringClientFactory匯入的RibbonClientConfiguration配置類優先順序高。
@Bean @ConditionalOnMissingBean public ServerListUpdater ribbonServerListUpdater(IClientConfig config) { return new PollingServerListUpdater(config); }
ServerListUpdater,就是我們剖析的PollingServerListUpdater,預設30s更新一次BaseLoadBalancer內部服務的快取。
@Bean @ConditionalOnMissingBean public ILoadBalancer ribbonLoadBalancer(IClientConfig config, ServerList<Server> serverList, ServerListFilter<Server> serverListFilter, IRule rule, IPing ping, ServerListUpdater serverListUpdater) { if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) { return this.propertiesFactory.get(ILoadBalancer.class, config, name); } return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList, serverListFilter, serverListUpdater); }
ILoadBalancer,預設是ZoneAwareLoadBalancer,構造的時候也傳入了上面宣告的的bean,ZoneAwareLoadBalancer這個類繼承了DynamicServerListLoadBalancer,所以這個類功能也符合我們剖析的原始碼,至於ZoneAwareLoadBalancer多餘的特性,也不用care。
到這裡,Ribbon在SpringCloud的配置我們就講完了,主要就是宣告瞭很多核心元件的bean,最後都設定到ZoneAwareLoadBalancer中。但是,AbstractLoadBalancerAwareClient這個物件的宣告我們並沒有在配置類中找到,主要是因為這個物件是OpenFeign整合Ribbon的一個入口,至於是如何整合的,這個坑就留給下篇文章吧。
那麼在springcloud中,上圖就可以加上註冊中心。
三、總結
本文剖析了Ribbon這個負載均衡元件中的一些核心元件的原始碼,並且將這些元件之間的關係一一描述清楚,同時也剖析了在傳送請求的時候是如何通過ILoadBalancer獲取到一個服務例項,重構URI的過程。希望本篇文章能夠讓你知道Ribbon是如何工作的。至於OpenFeign整合Ribbon,詳見文章 【SpringCloud原理】OpenFeign原來是這麼基於Ribbon來實現負載均衡的。
往期熱門文章推薦
掃碼或者搜尋關注公眾號 三友的java日記 ,及時乾貨不錯過,公眾號致力於通過畫圖加上通俗易懂的語言講解技術,讓技術更加容易學習。