亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Spring Cloud Ribbon負(fù)載均衡器處理方法

 更新時(shí)間:2018年02月26日 11:48:25   作者:白色的海  
這篇文章主要介紹了Spring Cloud Ribbon負(fù)載均衡器處理方法,看看是如何獲取服務(wù)實(shí)例,獲取以后做了哪些處理,處理后又是如何選取服務(wù)實(shí)例的,需要的朋友可以參考下

接下來(lái)擼一擼負(fù)載均衡器的內(nèi)部,看看是如何獲取服務(wù)實(shí)例,獲取以后做了哪些處理,處理后又是如何選取服務(wù)實(shí)例的。

分成三個(gè)部分來(lái)擼:

  • 配置
  • 獲取服務(wù)
  • 選擇服務(wù)

配置

在上一篇《擼一擼Spring Cloud Ribbon的原理》的配置部分可以看到默認(rèn)的負(fù)載均衡器是ZoneAwareLoadBalancer。

看一看配置類。

位置:

spring-cloud-netflix-core-1.3.5.RELEASE.jar
org.springframework.cloud.netflix.ribbon
RibbonClientConfiguration.class
@SuppressWarnings("deprecation")
@Configuration
@EnableConfigurationProperties
//Order is important here, last should be the default, first should be optional
// see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration {
// 略
 @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);
 }
// 略
}

在實(shí)例化ZoneAwareLoadBalancer的時(shí)候注入了,config、rule、ping、serverList、serverListFilter、serverListUpdater實(shí)例。

config:配置實(shí)例。

rule:負(fù)載均衡策略實(shí)例。

ping:ping實(shí)例。

serverList:獲取和更新服務(wù)的實(shí)例。

serverListFilter:服務(wù)過(guò)濾實(shí)例。

serverListUpdater:服務(wù)列表信息更新實(shí)例。

@SuppressWarnings("deprecation")
@Configuration
@EnableConfigurationProperties
//Order is important here, last should be the default, first should be optional
// see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653
@Import({OkHttpRibbonConfiguration.class, RestClientRibbonConfiguration.class, HttpClientRibbonConfiguration.class})
public class RibbonClientConfiguration {
 // 略
 @Bean
 @ConditionalOnMissingBean
 public IClientConfig ribbonClientConfig() {
 DefaultClientConfigImpl config = new DefaultClientConfigImpl();
 config.loadProperties(this.name);
 return config;
 }
 @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;
 }
 @Bean
 @ConditionalOnMissingBean
 public RibbonLoadBalancerContext ribbonLoadBalancerContext(
 ILoadBalancer loadBalancer, IClientConfig config, RetryHandler retryHandler) {
 return new RibbonLoadBalancerContext(loadBalancer, config, retryHandler);
 }
 // 略
}

在這里配置相關(guān)的實(shí)例

config:DefaultClientConfigImpl。

rule:ZoneAvoidanceRule。

ping:DummyPing。

serverList:ConfigurationBasedServerList,基于配置的服務(wù)列表實(shí)例。

serverListFilter:ZonePreferenceServerListFilter。

serverListUpdater:PollingServerListUpdater。

要注意的是,在這里serverList的實(shí)例是ConfigurationBasedServerList,這是在未使用Eureka時(shí)獲取服務(wù)信息的實(shí)例,是從配置文件中獲取。

那么在和Eureka配合使用時(shí),需要從 Eureka Server獲取服務(wù)信息,那該是哪個(gè)實(shí)例來(lái)做這件事情呢。

在啟用Eureka服務(wù)發(fā)現(xiàn)時(shí),會(huì)首先會(huì)采用EurekaRibbonClientConfiguration配置類。

位置:

spring-cloud-netflix-eureka-client-1.3.5.RELEASE.jar
org.springframework.cloud.netflix.ribbon.eureka
EurekaRibbonClientConfiguration.class
@Configuration
@CommonsLog
public class EurekaRibbonClientConfiguration {
 // 略
 @Bean
 @ConditionalOnMissingBean
 public IPing ribbonPing(IClientConfig config) {
 if (this.propertiesFactory.isSet(IPing.class, serviceId)) {
 return this.propertiesFactory.get(IPing.class, config, serviceId);
 }
 NIWSDiscoveryPing ping = new NIWSDiscoveryPing();
 ping.initWithNiwsConfig(config);
 return ping;
 }
 @Bean
 @ConditionalOnMissingBean
 public ServerList<?> ribbonServerList(IClientConfig config, Provider<EurekaClient> eurekaClientProvider) {
 if (this.propertiesFactory.isSet(ServerList.class, serviceId)) {
 return this.propertiesFactory.get(ServerList.class, config, serviceId);
 }
 DiscoveryEnabledNIWSServerList discoveryServerList = new DiscoveryEnabledNIWSServerList(
 config, eurekaClientProvider);
 DomainExtractingServerList serverList = new DomainExtractingServerList(
 discoveryServerList, config, this.approximateZoneFromHostname);
 return serverList;
 }
 // 略
}

在首先采用了EurekaRibbonClientConfiguration配置后,實(shí)際上各實(shí)例變成了

config:DefaultClientConfigImpl。

rule:ZoneAvoidanceRule。

ping:NIWSDiscoveryPing。

serverList:DomainExtractingServerList,內(nèi)部是DiscoveryEnabledNIWSServerList,實(shí)際上是通過(guò)服務(wù)發(fā)現(xiàn)獲取服務(wù)信息列表。

serverListFilter:ZonePreferenceServerListFilter。

serverListUpdater:PollingServerListUpdater。

獲取服務(wù)

在找到獲取服務(wù)信息入口前,先把負(fù)載均衡器的類繼承關(guān)系擼一下。

在ZoneAwareLoadBalancer的構(gòu)造中調(diào)用了父類DynamicServerListLoadBalancer構(gòu)造。

位置:

ribbon-loadbalancer-2.2.2.jar

com.netflix.loadbalancer

ZoneAwareLoadBalancer.class

在DynamicServerListLoadBalancer的構(gòu)造中,調(diào)用了restOfInit函數(shù)。

ribbon-loadbalancer-2.2.2.jar

com.netflix.loadbalancer

DynamicServerListLoadBalancer.class

 void restOfInit(IClientConfig clientConfig) {
 boolean primeConnection = this.isEnablePrimingConnections();
 // turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
 this.setEnablePrimingConnections(false);
 enableAndInitLearnNewServersFeature();
 updateListOfServers();
 if (primeConnection && this.getPrimeConnections() != null) {
 this.getPrimeConnections()
  .primeConnections(getReachableServers());
 }
 this.setEnablePrimingConnections(primeConnection);
 LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
 }

先是通過(guò)調(diào)用enableAndInitLearnNewServersFeature方法啟動(dòng)定時(shí)更新服務(wù)列表,然后立即調(diào)用updateListOfServers函數(shù)馬上獲取并更新服務(wù)列表信息。

先看下enableAndInitLearnNewServersFeature方法,實(shí)際上是調(diào)用了服務(wù)列表信息更新實(shí)例的start方法啟動(dòng)定時(shí)更新功能。

 /**
 * Feature that lets us add new instances (from AMIs) to the list of
 * existing servers that the LB will use Call this method if you want this
 * feature enabled
 */
 public void enableAndInitLearnNewServersFeature() {
 LOGGER.info("Using serverListUpdater {}", serverListUpdater.getClass().getSimpleName());
 serverListUpdater.start(updateAction);
 }

這里的服務(wù)列表信息更新實(shí)例就是配置階段配置的PollingServerListUpdater實(shí)例,看一下這個(gè)類的構(gòu)造和start方法。

public class PollingServerListUpdater implements ServerListUpdater {
 // 略
 private static long LISTOFSERVERS_CACHE_UPDATE_DELAY = 1000; // msecs;
 private static int LISTOFSERVERS_CACHE_REPEAT_INTERVAL = 30 * 1000; // msecs; 
 // 略
 private final AtomicBoolean isActive = new AtomicBoolean(false);
 private volatile long lastUpdated = System.currentTimeMillis();
 private final long initialDelayMs;
 private final long refreshIntervalMs;
 // 略
 public PollingServerListUpdater(IClientConfig clientConfig) {
 this(LISTOFSERVERS_CACHE_UPDATE_DELAY, getRefreshIntervalMs(clientConfig));
 }
 public PollingServerListUpdater(final long initialDelayMs, final long refreshIntervalMs) {
 this.initialDelayMs = initialDelayMs;
 this.refreshIntervalMs = refreshIntervalMs;
 }
 @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");
 }
 }
 // 略
}

從構(gòu)造和常量定義看出來(lái),延遲一秒執(zhí)行,默認(rèn)每隔30秒執(zhí)行更新,可以通過(guò)配置修改間隔更新的時(shí)間。

從start方法看,就是開(kāi)了一個(gè)定時(shí)執(zhí)行的schedule,定時(shí)執(zhí)行 updateAction.doUpdate()。

回到start方法調(diào)用方DynamicServerListLoadBalancer類中看一下UpdateAction實(shí)例的定義。

protected final ServerListUpdater.UpdateAction updateAction = new ServerListUpdater.UpdateAction() {
 @Override
 public void doUpdate() {
 updateListOfServers();
 }
 };

實(shí)際上就是調(diào)用了DynamicServerListLoadBalancer類的updateListOfServers方法,這跟啟動(dòng)完定時(shí)更新后立即更新服務(wù)信息列表的路徑是一致的。

 繼續(xù)看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);
 }

1.通過(guò)ServerList實(shí)例獲取服務(wù)信息列表。

2.通過(guò)ServerListFilter 實(shí)例對(duì)獲取到的服務(wù)信息列表進(jìn)行過(guò)濾。

3.將過(guò)濾后的服務(wù)信息列表保存到LoadBalancerStats中作為狀態(tài)保持。

接下分別看一下。

1.通過(guò)ServerList實(shí)例獲取服務(wù)信息列表。

ServerList實(shí)例就是配置階段生成的DomainExtractingServerList,獲取服務(wù)信息都是委托給DiscoveryEnabledNIWSServerList。

public class DiscoveryEnabledNIWSServerList extends AbstractServerList<DiscoveryEnabledServer>{
 // 略
 @Override
 public List<DiscoveryEnabledServer> getInitialListOfServers(){
 return obtainServersViaDiscovery();
 }
 @Override
 public List<DiscoveryEnabledServer> getUpdatedListOfServers(){
 return obtainServersViaDiscovery();
 }
 private List<DiscoveryEnabledServer> obtainServersViaDiscovery() {
 List<DiscoveryEnabledServer> serverList = new ArrayList<DiscoveryEnabledServer>();
 if (eurekaClientProvider == null || eurekaClientProvider.get() == null) {
 logger.warn("EurekaClient has not been initialized yet, returning an empty list");
 return new ArrayList<DiscoveryEnabledServer>();
 }
 EurekaClient eurekaClient = eurekaClientProvider.get();
 if (vipAddresses!=null){
 for (String vipAddress : vipAddresses.split(",")) {
 // if targetRegion is null, it will be interpreted as the same region of client
 List<InstanceInfo> listOfInstanceInfo = eurekaClient.getInstancesByVipAddress(vipAddress, isSecure, targetRegion);
 for (InstanceInfo ii : listOfInstanceInfo) {
  if (ii.getStatus().equals(InstanceStatus.UP)) {
  if(shouldUseOverridePort){
  if(logger.isDebugEnabled()){
  logger.debug("Overriding port on client name: " + clientName + " to " + overridePort);
  }
  // copy is necessary since the InstanceInfo builder just uses the original reference,
  // and we don't want to corrupt the global eureka copy of the object which may be
  // used by other clients in our system
  InstanceInfo copy = new InstanceInfo(ii);
  if(isSecure){
  ii = new InstanceInfo.Builder(copy).setSecurePort(overridePort).build();
  }else{
  ii = new InstanceInfo.Builder(copy).setPort(overridePort).build();
  }
  }
  DiscoveryEnabledServer des = new DiscoveryEnabledServer(ii, isSecure, shouldUseIpAddr);
  des.setZone(DiscoveryClient.getZone(ii));
  serverList.add(des);
  }
 }
 if (serverList.size()>0 && prioritizeVipAddressBasedServers){
  break; // if the current vipAddress has servers, we dont use subsequent vipAddress based servers
 }
 }
 }
 return serverList;
 }
 // 略
}

可以看到其實(shí)就是通過(guò)Eureka客戶端從Eureka服務(wù)端獲取所有服務(wù)實(shí)例信息并把上線的包裝成DiscoveryEnabledServer實(shí)例,帶有zone信息,做到服務(wù)列表中。

2.通過(guò)ServerListFilter 實(shí)例對(duì)獲取到的服務(wù)信息列表進(jìn)行過(guò)濾。

serverListFilte實(shí)例就是配置階段生成的ZonePreferenceServerListFilter,通過(guò)調(diào)用該實(shí)例的getFilteredListOfServers方法進(jìn)行過(guò)濾。

@Data
@EqualsAndHashCode(callSuper = false)
public class ZonePreferenceServerListFilter extends ZoneAffinityServerListFilter<Server> {
 private String zone;
 @Override
 public void initWithNiwsConfig(IClientConfig niwsClientConfig) {
 super.initWithNiwsConfig(niwsClientConfig);
 if (ConfigurationManager.getDeploymentContext() != null) {
 this.zone = ConfigurationManager.getDeploymentContext().getValue(
  ContextKey.zone);
 }
 }
 @Override
 public List<Server> getFilteredListOfServers(List<Server> servers) {
 List<Server> output = super.getFilteredListOfServers(servers);
 if (this.zone != null && output.size() == servers.size()) {
 List<Server> local = new ArrayList<Server>();
 for (Server server : output) {
 if (this.zone.equalsIgnoreCase(server.getZone())) {
  local.add(server);
 }
 }
 if (!local.isEmpty()) {
 return local;
 }
 }
 return output;
 }
}

在getFilteredListOfServers方法里面,一上來(lái)是調(diào)用父類的同名方法先過(guò)濾,其實(shí)父類也是把和消費(fèi)端同區(qū)域的服務(wù)給過(guò)濾出來(lái)使用,不僅如此,增加了些智能的判定,保證在故障/負(fù)載較高時(shí)或者可用實(shí)例較少時(shí)不進(jìn)行同區(qū)域的過(guò)濾。

但是在ZonePreferenceServerListFilter.getFilteredListOfServers這里,就算父類沒(méi)做過(guò)過(guò)濾,這里依然要把同zone的服務(wù)給濾出來(lái)使用,誰(shuí)叫這里的類是ZonePreference的呢。

這是比較怪異的地方,感覺(jué)父類的智能判定沒(méi)什么作用。

還是看看ZoneAffinityServerListFilter.getFilteredListOfServers做的辛苦工作吧。

public class ZoneAffinityServerListFilter<T extends Server> extends
 AbstractServerListFilter<T> implements IClientConfigAware {
 // 略
 private boolean shouldEnableZoneAffinity(List<T> filtered) { 
 if (!zoneAffinity && !zoneExclusive) {
 return false;
 }
 if (zoneExclusive) {
 return true;
 }
 LoadBalancerStats stats = getLoadBalancerStats();
 if (stats == null) {
 return zoneAffinity;
 } else {
 logger.debug("Determining if zone affinity should be enabled with given server list: {}", filtered);
 ZoneSnapshot snapshot = stats.getZoneSnapshot(filtered);
 double loadPerServer = snapshot.getLoadPerServer();
 int instanceCount = snapshot.getInstanceCount(); 
 int circuitBreakerTrippedCount = snapshot.getCircuitTrippedCount();
 if (((double) circuitBreakerTrippedCount) / instanceCount >= blackOutServerPercentageThreshold.get() 
  || loadPerServer >= activeReqeustsPerServerThreshold.get()
  || (instanceCount - circuitBreakerTrippedCount) < availableServersThreshold.get()) {
 logger.debug("zoneAffinity is overriden. blackOutServerPercentage: {}, activeReqeustsPerServer: {}, availableServers: {}", 
  new Object[] {(double) circuitBreakerTrippedCount / instanceCount, loadPerServer, instanceCount - circuitBreakerTrippedCount});
 return false;
 } else {
 return true;
 }
 }
 }
 @Override
 public List<T> getFilteredListOfServers(List<T> servers) {
 if (zone != null && (zoneAffinity || zoneExclusive) && servers !=null && servers.size() > 0){
 List<T> filteredServers = Lists.newArrayList(Iterables.filter(
  servers, this.zoneAffinityPredicate.getServerOnlyPredicate()));
 if (shouldEnableZoneAffinity(filteredServers)) {
 return filteredServers;
 } else if (zoneAffinity) {
 overrideCounter.increment();
 }
 }
 return servers;
 }
 // 略
}

首先會(huì)將與消費(fèi)端相同的zone的服務(wù)過(guò)濾出來(lái),然后通過(guò)shouldEnableZoneAffinity(filteredServers)來(lái)判定是否可以采納同zone的服務(wù),還是采用所有的服務(wù)。

在shouldEnableZoneAffinity方法內(nèi),對(duì)相同zone的服務(wù)做了一次snapshot,獲取這些服務(wù)的實(shí)例數(shù)量,平均負(fù)載,斷路的實(shí)例數(shù)進(jìn)行計(jì)算判定。

可以看一下initWithNiwsConfig方法中關(guān)鍵指標(biāo)的值。

判定條件:

斷路實(shí)例百分比>=0.8(斷路的實(shí)例數(shù)/服務(wù)的實(shí)例數(shù)量)

平均負(fù)載>=0.6

可用實(shí)例數(shù)<2(實(shí)例數(shù)量-斷路的實(shí)例數(shù))

如果達(dá)到判定條件,那么就使用全部的服務(wù),保證可用性。

但,上面也說(shuō)了,因?yàn)閆onePreferenceServerListFilter本身總是會(huì)選用和消費(fèi)端zone一致的服務(wù),所以ZoneAffinityServerListFilter.getFilteredListOfServers中做的智能操作并沒(méi)什么用。

不過(guò),當(dāng)然可以通過(guò)自定義配置來(lái)采用ZoneAffinityServerListFilter實(shí)例。

3.將過(guò)濾后的服務(wù)信息列表保存到LoadBalancerStats中作為狀態(tài)保持。

跟進(jìn)updateAllServerList(servers);去,一步步深入,會(huì)發(fā)現(xiàn),實(shí)際上是保存到LoadBalancerStats中,并且這時(shí)候的服務(wù)是按照zone分組以HashMap<String, List<Server>>結(jié)構(gòu)保存的,key是zone。

選擇服務(wù)

實(shí)現(xiàn)了ILoadBalancer接口的負(fù)載均衡器,是通過(guò)實(shí)現(xiàn)chooseServer方法來(lái)進(jìn)行服務(wù)的選擇,選擇后的服務(wù)做為目標(biāo)請(qǐng)求服務(wù)。

看一下ZoneAwareLoadBalancer.chooseServer方法。

@Override
 public Server chooseServer(Object key) {
 if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
 logger.debug("Zone aware logic disabled or there is only one zone");
 return super.chooseServer(key);
 }
 Server server = null;
 try {
 LoadBalancerStats lbStats = getLoadBalancerStats();
 Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
 logger.debug("Zone snapshots: {}", zoneSnapshot);
 if (triggeringLoad == null) {
 triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty(
  "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".triggeringLoadPerServerThreshold", 0.2d);
 }

 if (triggeringBlackoutPercentage == null) {
 triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty(
  "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".avoidZoneWithBlackoutPercetage", 0.99999d);
 }
 Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());
 logger.debug("Available zones: {}", availableZones);
 if (availableZones != null && availableZones.size() < zoneSnapshot.keySet().size()) {
 String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);
 logger.debug("Zone chosen: {}", zone);
 if (zone != null) {
  BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone);
  server = zoneLoadBalancer.chooseServer(key);
 }
 }
 } catch (Exception e) {
 logger.error("Error choosing server using zone aware logic for load balancer={}", name, e);
 }
 if (server != null) {
 return server;
 } else {
 logger.debug("Zone avoidance logic is not invoked.");
 return super.chooseServer(key);
 }
 }

注意這里有兩種用法:

1.通過(guò)配置ZoneAwareNIWSDiscoveryLoadBalancer.enabled=false關(guān)閉區(qū)域感知負(fù)載均衡,或者zone的個(gè)數(shù)<=1個(gè)。

2.采用區(qū)域感知,或者zone的個(gè)數(shù)>1。

一個(gè)個(gè)來(lái)看一下

1.通過(guò)配置ZoneAwareNIWSDiscoveryLoadBalancer.enabled=false關(guān)閉區(qū)域感知負(fù)載均衡,或者zone的個(gè)數(shù)<=1個(gè)。

這種情況下,調(diào)用了父類BaseLoadBalancer.chooseServer方法。

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

這里使用的負(fù)載均衡策略rule實(shí)際上就是構(gòu)造ZoneAwareLoadBalancer時(shí)傳進(jìn)來(lái)的,在配置階段生成的ZoneAvoidanceRule策略實(shí)例。   

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

假設(shè),如果沒(méi)有配置,默認(rèn)用的是RoundRobinRule策略實(shí)例。

2.采用區(qū)域感知,或者zone的個(gè)數(shù)>1。

public Server chooseServer(Object key) {
 if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
 logger.debug("Zone aware logic disabled or there is only one zone");
 return super.chooseServer(key);
 }
 Server server = null;
 try {
 LoadBalancerStats lbStats = getLoadBalancerStats();
 Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
 logger.debug("Zone snapshots: {}", zoneSnapshot);
 if (triggeringLoad == null) {
 triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty(
  "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".triggeringLoadPerServerThreshold", 0.2d);
 }

 if (triggeringBlackoutPercentage == null) {
 triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty(
  "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".avoidZoneWithBlackoutPercetage", 0.99999d);
 }
 Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());
 logger.debug("Available zones: {}", availableZones);
 if (availableZones != null && availableZones.size() < zoneSnapshot.keySet().size()) {
 String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);
 logger.debug("Zone chosen: {}", zone);
 if (zone != null) {
  BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone);
  server = zoneLoadBalancer.chooseServer(key);
 }
 }
 } catch (Exception e) {
 logger.error("Error choosing server using zone aware logic for load balancer={}", name, e);
 }
 if (server != null) {
 return server;
 } else {
 logger.debug("Zone avoidance logic is not invoked.");
 return super.chooseServer(key);
 }
 }

在這種情況下默認(rèn)使用ZoneAvoidanceRule負(fù)載均衡策略。

獲取zone的snapshot信息。

獲取可用的zone,通過(guò)觀察ZoneAvoidanceRule.getAvailableZones定義,不是可用zone的條件是:

  • 所屬實(shí)例數(shù)==0。
  • 故障率>0.99999或者平均負(fù)載<0。
  • 如果不是上面兩種情況,就選擇負(fù)載最高的一個(gè)去除不作為可用的zone。

可用zone都獲取后,隨機(jī)選一個(gè)。

并從該zone中,通過(guò)ZoneAwareLoadBalancer的父類BaseLoadBalancer.chooseServer選取服務(wù),上面整理過(guò),BaseLoadBalancer里如果沒(méi)有傳入rule,那么默認(rèn)使用RoundRobinRule策略輪尋一個(gè)服務(wù)。

其實(shí),還是上面獲取服務(wù)中ZonePreferenceServerListFilter過(guò)濾器的問(wèn)題,實(shí)際上過(guò)濾出來(lái)的只有一個(gè)和消費(fèi)端相同的一個(gè)zone的服務(wù),所以第2.部分的從可用zone中選取服務(wù)的功能是走不到,要走到就得把過(guò)濾器給換掉。

總結(jié):

配置的負(fù)載均衡器會(huì)啟動(dòng)schedule獲取服務(wù)信息,在使用了Eureka客戶端時(shí),會(huì)從Eureka服務(wù)獲取所有服務(wù)實(shí)例信息,通過(guò)過(guò)濾器過(guò)濾出可以使用的服務(wù),過(guò)濾器默認(rèn)只過(guò)濾出與消費(fèi)端相同zone的服務(wù),如果要保證高可用可配置ZoneAffinityServerListFilter過(guò)濾器,過(guò)濾后的服務(wù)列表,通過(guò)實(shí)現(xiàn)了IRule接口的負(fù)載均衡策略選取對(duì)應(yīng)的服務(wù),如果是使用zone感知的策略,可以從負(fù)載情況良好的zone中選取合適的服務(wù)。

相關(guān)文章

  • SpringBoot Test及注解的使用詳解

    SpringBoot Test及注解的使用詳解

    這篇文章主要介紹了SpringBoot Test及注解的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • Java對(duì)象不使用時(shí)賦值null的意義詳解

    Java對(duì)象不使用時(shí)賦值null的意義詳解

    這篇文章主要介紹了java對(duì)象不再使用時(shí)賦值null的意義,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-03-03
  • 詳解SpringBoot如何自定義啟動(dòng)畫面

    詳解SpringBoot如何自定義啟動(dòng)畫面

    當(dāng)我們?cè)趩?dòng)SpringBoot項(xiàng)目時(shí)候會(huì)在控制臺(tái)上看到一些單調(diào)的圖案,有些朋友覺(jué)得這些圖案很單調(diào),那我們是否可以自定義啟動(dòng)畫面呢,接下來(lái)小編就給大家介紹一下SpringBoot是如何實(shí)現(xiàn)自定義啟動(dòng)畫面,感興趣的同學(xué)跟著小編一起來(lái)看看吧
    2023-07-07
  • resty upload無(wú)需依賴的文件上傳與下載

    resty upload無(wú)需依賴的文件上傳與下載

    這篇文章主要為大家介紹了resty upload中無(wú)需依賴的文件上傳與下載過(guò)程,有需要的朋友可以借鑒參考下,希望能夠有所幫助祝大家多多進(jìn)步,早日升職加薪
    2022-03-03
  • 基于springboot2集成jpa,創(chuàng)建dao的案例

    基于springboot2集成jpa,創(chuàng)建dao的案例

    這篇文章主要介紹了基于springboot2集成jpa,創(chuàng)建dao的案例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-01-01
  • spring基于通用Dao的多數(shù)據(jù)源配置詳解

    spring基于通用Dao的多數(shù)據(jù)源配置詳解

    這篇文章主要為大家詳細(xì)介紹了spring基于通用Dao的多數(shù)據(jù)源配置,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下解
    2018-03-03
  • Java多線程之鎖學(xué)習(xí)(增強(qiáng)版)

    Java多線程之鎖學(xué)習(xí)(增強(qiáng)版)

    這篇文章主要為大家詳細(xì)介紹了Java多線程中鎖的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),對(duì)我們了解線程有一定幫助,需要的可以參考一下
    2023-02-02
  • springboot的jar包如何啟用外部配置文件

    springboot的jar包如何啟用外部配置文件

    本文主要介紹了springboot的jar包如何啟用外部配置文件,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-06-06
  • 解決Nacos成功啟動(dòng)但是無(wú)法訪問(wèn) (Connection refused)

    解決Nacos成功啟動(dòng)但是無(wú)法訪問(wèn) (Connection refused)

    這篇文章主要介紹了解決Nacos成功啟動(dòng)但是無(wú)法訪問(wèn) (Connection refused)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-06-06
  • java objectUtils 使用可能會(huì)出現(xiàn)的問(wèn)題

    java objectUtils 使用可能會(huì)出現(xiàn)的問(wèn)題

    這篇文章主要介紹了java objectUtils 使用可能會(huì)出現(xiàn)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-02-02

最新評(píng)論