大家好,本文我將繼續來剖析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來實作負載均衡的,
往期熱門文章推薦
-
Redis分布式鎖實作Redisson 15問
-
Zookeeper分布式鎖實作Curator十一問
-
有關回圈依賴和三級快取的這些問題,你都會么?(面試常問)
-
萬字+28張圖帶你探秘小而美的規則引擎框架LiteFlow
-
7000字+24張圖帶你徹底弄懂執行緒池
- 面渣逆襲:Spring三十五問,四萬字+五十圖詳解!建議收藏!
掃碼或者搜索關注公眾號 三友的java日記 ,及時干貨不錯過,公眾號致力于通過畫圖加上通俗易懂的語言講解技術,讓技術更加容易學習,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/491879.html
標籤:Java
下一篇:MyBatis流式查詢

