天天看點

深入了解SpringCloud源碼探究篇 | ribbon源碼分析

SpringCloud系列:

  1. SpringCloud入門必看例子
  2. 深入了解SpringCloud源碼探究篇 | Eureka服務端源碼分析
  3. 深入了解SpringCloud源碼探究篇 | Eureka用戶端源碼分析
  4. 深入了解SpringCloud源碼探究篇 | ribbon源碼分析
  5. 深入了解SpringCloud源碼探究篇 | Feign源碼分析

下面先利用白話文來描述下ribbon的工作流程

Ribbon:

    自動配置類RibbonClientConfiguration和EurekaRibbonClientConfiguration配置類進行各bean轉載包括以下五大元件和ZoneAwareLoadBalancer初始化操作

    五大元件:

        ServerList:定義擷取伺服器清單 ,預設實作DiscoveryEnabledNIWSServerList

        ServerListFilter:對ServerList伺服器清單進行二次過濾 ,預設實作ZonePreferenceServerListFilter

        ServerListUpdater: 定義服務更新政策 ,預設實作PollingServerListUpdater

        IPing: 檢查服務清單是否存活 預設實作NIWSDiscoveryPing

        IRule :根據算法中從服務清單中選取一個要通路的服務 ,預設實作RoundRobinRule

    ZoneAwareLoadBalancer: (ILoadBalancer預設的實作),ZoneAwareLoadBalancer是DynamicServerListLoadBalancer的子類,增加zone政策,DynamicServerListLoadBalancer對以上五大元件進行組合使用,調用ServerList接口,DiscoveryEnabledNIWSServerList(預設配置).getInitialListOfServers:從eureka用戶端服務清單緩存中(DiscoveryClient.localRegionApps)擷取服務清單 ;

    ServerListUpdater定義服務更新政策 (預設PollingServerListUpdater),這裡DynamicServerListLoadBalancer初始化過程中會傳入自己實作的ServerListUpdater.UpdateAction對象調用ServerListUpdater.start(UpdateAction)啟動一個定時任務,任務中程序調用傳入UpdateAction.doUpdate方法,實際調用的是DynamicServerListLoadBalancer.updateListOfServers方法然後調用ServerList的getUpdatedListOfServers方法進行服務拉取更新,接着updateListOfServers方法裡拿到的服務清單之後會進行調用ServerListFilter的getFilteredListOfServers進行過濾篩選。總而言之就是DynamicServerListLoadBalancer初始化過程中會調用PollingServerListUpdater.start啟動一個定時任務進行實時調用DiscoveryEnabledNIWSServerList.getUpdatedListOfServers去獲DiscoveryClient裡的服務清單緩存(DiscoveryClient.localRegionApps)取最新服務清單接着通過ServerListFilter進行過濾,然後将服務清單setServersList放在自己的緩存allServerList裡(這個是BaseLoadBalancer的allServerList)。接着就是IRule :根據算法中從服務清單中選取一個要通路的服務 ,預設RoundRobinRule輪詢算法。DynamicServerListLoadBalancer實作了ILoadBalancer的chooseServer方法,該實作方法裡調用的就是IRule.choose,自然這裡說的就是RoundRobinRule實作的方法choose,choose調用BaseLoadBalancer擷取到服務緩存清單allServerList,然後進過一系列操作選擇出一個調用的服務進行傳回。 

以上提到的五大元件包括ILoadBalancer都是可以通過配置來進行自由選擇實作類:

# 負載均衡類 
{服務名}.ribbon.NFLoadBalancerClassName=ZoneAwareLoadBalancer
# 負載均衡規則類
{服務名}.ribbon.NFLoadBalancerRuleClassName=AvailabilityFilteringRule
# 心跳檢測類
{服務名}.ribbon.NFLoadBalancerPingClassName=NIWSDiscoveryPing
# 服務清單類
{服務名}.ribbon.NIWSServerListClassName=DiscoveryEnabledNIWSServerList
# 服務過濾類
{服務名}.ribbon.NIWSServerListFilterClassName=ZonePreferenceServerListFilter
           

RibbonClientConfiguration配置類

@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;
	}

	@Bean
	@ConditionalOnMissingBean
	public IPing ribbonPing(IClientConfig config) {
		if (this.propertiesFactory.isSet(IPing.class, name)) {
			return this.propertiesFactory.get(IPing.class, config, name);
		}
		return new DummyPing();
	}

	@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;
	}

	@Bean
	@ConditionalOnMissingBean
	public ServerListUpdater ribbonServerListUpdater(IClientConfig config) {
		return new PollingServerListUpdater(config);
	}

	@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);
	}

	@Bean
	@ConditionalOnMissingBean
	@SuppressWarnings("unchecked")
	public ServerListFilter<Server> ribbonServerListFilter(IClientConfig config) {
		if (this.propertiesFactory.isSet(ServerListFilter.class, name)) {
			return this.propertiesFactory.get(ServerListFilter.class, config, name);
		}
		ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
		filter.initWithNiwsConfig(config);
		return filter;
	}
           

EurekaRibbonClientConfiguration配置類

深入了解SpringCloud源碼探究篇 | ribbon源碼分析

這兩個配置類中我們可以發現初始化了ribbon的幾大元件;這裡我們可以看到先是從propertiesFactory配置中擷取對應元件的自定義配置:

深入了解SpringCloud源碼探究篇 | ribbon源碼分析
深入了解SpringCloud源碼探究篇 | ribbon源碼分析

以上也證明我們前面提到的可以進行自定義配置個大元件的實作類

 ServerList接口

有兩個方法,第一個為初始服務清單擷取,第二個方法為更新緩存服務清單 

深入了解SpringCloud源碼探究篇 | ribbon源碼分析

ServerList定義擷取伺服器清單 ,預設實作DiscoveryEnabledNIWSServerList:

深入了解SpringCloud源碼探究篇 | ribbon源碼分析

這裡兩個實作方法均是調用obtainServersViaDiscovery方法進行服務清單拉取:

深入了解SpringCloud源碼探究篇 | ribbon源碼分析

 通過調用DiscoveryClient.getInstancesByVipAddress擷取InstanceInfo服務資訊清單:

深入了解SpringCloud源碼探究篇 | ribbon源碼分析

從上面的調用上可以看到,從 DiscoveryClient的localRegionApps緩存中擷取到服務清單資訊然後進行傳回

ServerListFilter接口

僅提供一個方法 

深入了解SpringCloud源碼探究篇 | ribbon源碼分析

ServerListFilter對ServerList伺服器清單進行二次過濾 ,預設實作ZonePreferenceServerListFilter

ServerListUpdater接口

深入了解SpringCloud源碼探究篇 | ribbon源碼分析

ServerListUpdater定義服務更新政策,預設實作PollingServerListUpdater:

實作start方法進行開啟一個定時任務,目的就是重新整理ribbon服務清單緩存

深入了解SpringCloud源碼探究篇 | ribbon源碼分析

實際上調用的是傳入的UpdateAction的doUpdate()方法,該方法在DynamicServerListLoadBalancer被實作重寫,後面再描述

IRule接口

深入了解SpringCloud源碼探究篇 | ribbon源碼分析

IRule根據算法從服務清單中選取一個要通路的服務 ,預設實作RoundRobinRule輪詢:

深入了解SpringCloud源碼探究篇 | ribbon源碼分析
深入了解SpringCloud源碼探究篇 | ribbon源碼分析

以上可以清楚看到從ILoadBalancer的緩存清單allServerList裡擷取出所有的服務,然後進行輪詢選擇一個服務進行傳回

ILoadBalancer

ILoadBalancer是ribbon整個的核心,相當于一個中央控制器,預設實作ZoneAwareLoadBalancer:

深入了解SpringCloud源碼探究篇 | ribbon源碼分析

ZoneAwareLoadBalancer是DynamicServerListLoadBalancer的子類,增加zone政策,DynamicServerListLoadBalancer對以上五大元件進行組合使用:

我們先來看DynamicServerListLoadBalancer該類被初始化是做了哪些具體操作:

深入了解SpringCloud源碼探究篇 | ribbon源碼分析

其他的操作均是将五大元件bean指派于DynamicServerListLoadBalancer中,我們重點來看看restOfInit:

深入了解SpringCloud源碼探究篇 | ribbon源碼分析

該方法調用enableAndInitLearnNewServersFeature以及updateListOfServers,我們先看enableAndInitLearnNewServersFeature:

深入了解SpringCloud源碼探究篇 | ribbon源碼分析

這裡我們可以清楚看到它調用了ServerListUpdater的start方法,start我們前面已經講到就是啟動了一個定時任務,具體操作線程我們需要看這個傳入的UpdateAction的具體實作,DynamicServerListLoadBalancer實作了ServerListUpdater.UpdateAction:

深入了解SpringCloud源碼探究篇 | ribbon源碼分析

然後doUpdate實際調用的是自己的updateListOfServers方法:

深入了解SpringCloud源碼探究篇 | ribbon源碼分析

以上可以清楚看到利用ServerList的實作DiscoveryEnabledNIWSServerList調用getUpdatedListOfServers,該方法前面已經分析過,就是向eureka用戶端的localRegionApps緩存進行服務的拉取操作然後傳回服務清單。

接着調用ServerListFilter的實作進行二次過濾,這裡我就不具體分析這個了。接着就是調用updateAllServerList(servers),然後接着setServersList對服務清單放入進BaseLoadBalancer的allServerList裡

深入了解SpringCloud源碼探究篇 | ribbon源碼分析

到這裡也就是說DynamicServerListLoadBalancer初始化過程中調用ServerListUpdater.start方法進行啟動一個定時任務來調用updateListOfServers方法,進行服務拉取操作以及過濾。我們回到restOfInit方法啟動任務之後接着下一步還是調用updateListOfServers,其實就是一起動就去拉取服務清單。總而言之就是初始化過程進行服務拉取進自己的allServerList緩存清單中,然後定時進行服務拉取更新操作。

ILoadBalancer的對外提供中比較主要的就是chooseServer方法,用來服務挑選,我們直接看DynamicServerListLoadBalancer的父類BaseLoadBalancer的chooseServer方法:

深入了解SpringCloud源碼探究篇 | ribbon源碼分析

以上可以看到利用IRule實作的choose進行服務挑選,前面初始化時已經将其實作類RoundRobinRule指派了進來,前面也已經提到過RoundRobinRule的choose方法,這裡不再贅述。

總結

ILoadBalancer被預設初始化ZoneAwareLoadBalancer,然後接着進行向eureka用戶端的localRegionApps緩存中擷取服務清單接着過濾後放入自己的緩存allServerList裡。接着調用ServerListUpdater.start啟動一個定時任務進行服務的拉取更新操作和過濾。在對外提供使用過程中調用chooseServer方法進行調用IRule的choose方法進行服務的挑選和具體服務傳回。

繼續閱讀