Spring Cloud Ribbon源码解析

1 概述

Spring Cloud Ribbon 可以实现客户端负载均衡,本文仅仅以Ribbon单独使用时对源码进行分析和理解,没有集成Eureka。
Ribbon会针对我们在配置文件中配置的服务地址进行负载均衡的计算,得到目标地址后,进行服务的调用。
接下来会针对两方面进行分析:
1、为什么我们使用@LoadBalanced注解作用于RestTemplate上就可以实现负载均衡了呢?
2、如何对服务地址进行解析的呢?

2 版本信息

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            <version>2.2.9.RELEASE</version>
        </dependency>

3 源码解析

3.1 @LoadBalancer注解的作用

在使用RestTemplate的时候,我们加了一个@LoadBalance注解,就能让这个RestTemplate在请求时,就拥有客户端负载均衡的能力。

@Bean
@LoadBalanced
RestTemplate restTemplate() {
	return new RestTemplate();
}

然后,我们打开@LoadBalanced这个注解,可以发现该注解仅仅是声明了一个@Qualifier 注解。

@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}

@Qualifier注解相信大家在spring中都接触过,起作用就是当您创建多个相同类型的 bean 并希望仅使用属性装配其中一个 bean 时,您可以使用@Qualifier 注解和 @Autowired 通过指定应该装配哪个确切的 bean来消除歧义。

下面我们进行一个案例演示:
现在我们有一个TestClass类,想把这个类的实例对象注入到Spring容器中

public class TestClass {
private String name;
}
@Configuration
public class TestConfig {
	@Bean("testClass1")
	TestClass testClass(){
		return new TestClass("testClass1");
	}
	@Bean("testClass2")
	TestClass testClass2(){
		return new TestClass("testClass2");
	}
}

@RestController
public class TestController {
	@Autowired(required = false)
	List<TestClass> testClasses= Collections.emptyList();
	@GetMapping("/test")
	public Object test(){
		return testClasses;
	}
}

通过浏览器访问http://localhost:8080/test

[
	{
	name: "testClass1"
	},
	{
	name: "testClass2"
	}
]

此时我们修改修改TestConfig类

@Configuration
public class TestConfig {
	@Bean("testClass1")
	@Qualifier
	TestClass testClass(){
		return new TestClass("testClass1");
	}
	@Bean("testClass2")
	TestClass testClass2(){
		return new TestClass("testClass2");
	}
}

再次进行访问可以发现:

[
	{
	name: "testClass1"
	}
]

通过这个案例我们是不是可以发现@LoadBalance注解的作用了,肯定会有个地方对加上了@LoadBanlance注解的RestTemplate进行筛选。答案肯定是没错的,确实有筛选的地方,下面我们就分析是哪里进行保存的呢?
我们找到spring-cloud-commons-2.2.9.RELEASE.jar这个包
找到META-INF目录。相信大家并不陌生,这里肯定使用的是spi的机制把一些类加载到IOC容器中的,我们打开spring.factories文件:

# AutoConfiguration
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.client.CommonsClientAutoConfiguration,\
org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration,\
org.springframework.cloud.client.discovery.composite.CompositeDiscoveryClientAutoConfiguration,\
org.springframework.cloud.client.discovery.composite.reactive.ReactiveCompositeDiscoveryClientAutoConfiguration,\
org.springframework.cloud.client.discovery.noop.NoopDiscoveryClientAutoConfiguration,\
org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration,\
org.springframework.cloud.client.discovery.simple.reactive.SimpleReactiveDiscoveryClientAutoConfiguration,\
org.springframework.cloud.client.hypermedia.CloudHypermediaAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.reactive.LoadBalancerBeanPostProcessorAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.reactive.ReactorLoadBalancerClientAutoConfiguration,\
org.springframework.cloud.client.loadbalancer.reactive.ReactiveLoadBalancerAutoConfiguration,\
org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration,\
org.springframework.cloud.commons.httpclient.HttpClientConfiguration,\
org.springframework.cloud.commons.util.UtilAutoConfiguration,\
org.springframework.cloud.configuration.CompatibilityVerifierAutoConfiguration,\
org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration
# Environment Post Processors
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.client.HostInfoEnvironmentPostProcessor
# Failure Analyzers
org.springframework.boot.diagnostics.FailureAnalyzer=\
org.springframework.cloud.configuration.CompatibilityNotMetFailureAnalyzer

我们发现有一个.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration类。
该类中会做两件事情:

  1. 把配置了@LoadBalanced 注解的RestTemplate 注入到restTemplates 集合中
  2. 拿到了RestTemplate 之后,在LoadBalancerInterceptorConfig配置类中,会针对这些RestTemplate 进行拦截,当我们调用目标方法的时候会通过拦截器进行拦截

具体的源码如下:

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {

	//收集配置了@LoadBalanced 注解的RestTemplate
	@LoadBalanced
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();
	
...省略

//拿到了RestTemplate 之后,在LoadBalancerInterceptorConfig配置类中,会针对这些RestTemplate 进行拦截,当我们调用目标方法的时候会通过拦截器进行拦截
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
	static class LoadBalancerInterceptorConfig {

		//装载一个LoadBalancerInterceptor的实例到IOC容器。
		@Bean
		public LoadBalancerInterceptor loadBalancerInterceptor(
				LoadBalancerClient loadBalancerClient,
				LoadBalancerRequestFactory requestFactory) {
			return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
		}
//遍历所有加了@LoadBalanced注解的RestTemplate,在原有的拦截器之上,再增加了一个LoadBalancerInterceptor
		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
				List<ClientHttpRequestInterceptor> list = new ArrayList<>(
						restTemplate.getInterceptors());
				list.add(loadBalancerInterceptor);
				restTemplate.setInterceptors(list);
			};
		}

	}
...
3.2 RestTemplate调用过程

接下来我们通过RestTemplate调用过程分析整个流程,在调用的过程中肯定会进入到我们上面注入的拦截器,进行拦截。

我们在程序中,使用下面的代码发起远程请求时restTemplate.getForObject(url,String.class);
整个调用过程如下:(我们重点分析拦截器部分,restTemplate源码不做重点)

RestTemplate.getForObject
-----> AbstractClientHttpRequest.execute()
-----> AbstractBufferingClientHttpRequest.executeInternal()
-----> InterceptingClientHttpRequest.executeInternal()
-----> InterceptingClientHttpRequest.execute()
3.3 LoadBalancerInterceptor拦截器

LoadBalancerInterceptor是一个拦截器,当一个被@Loadbalanced 注解修饰的RestTemplate 对象发起HTTP请求时,会被LoadBalancerInterceptor 的intercept 方法拦截,在这个方法中直接通过getHost 方法就可以获取到服务名(因为我们在使用RestTemplate调用服务的时候,使用的是服务名而不是域名,所以这里可以通过getHost直接拿到服务名然后去调用execute方法发起请求)

  • InterceptingClientHttpRequest.execute()
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
			//遍历所有的拦截器,通过拦截器进行逐个处理。
            if(this.iterator.hasNext()) {
                ClientHttpRequestInterceptor nextInterceptor = (ClientHttpRequestInterceptor)this.iterator.next();
                //调用intercept方法进行拦截 重点分析
                return nextInterceptor.intercept(request, body, this);
            } else {
                HttpMethod method = request.getMethod();
                Assert.state(method != null, "No standard HTTP method");
                ClientHttpRequest delegate = InterceptingClientHttpRequest.this.requestFactory.createRequest(request.getURI(), method);
                request.getHeaders().forEach((key, value) -> {
                    delegate.getHeaders().addAll(key, value);
                });
                if(body.length > 0) {
                    if(delegate instanceof StreamingHttpOutputMessage) {
                        StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage)delegate;
                        streamingOutputMessage.setBody((outputStream) -> {
                            StreamUtils.copy(body, outputStream);
                        });
                    } else {
                        StreamUtils.copy(body, delegate.getBody());
                    }
                }

                return delegate.execute();
            }
        }
  • InterceptingClientHttpRequest.intercept()
	@Override
	public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
			final ClientHttpRequestExecution execution) throws IOException {
		//获取请求的uri
		final URI originalUri = request.getURI();
		//获取服务名称,因为我们配置文件中配置的就是服务名
		String serviceName = originalUri.getHost();
		Assert.state(serviceName != null,
				"Request URI does not contain a valid hostname: " + originalUri);
		//private LoadBalancerClient loadBalancer; LoadBalancerClient其实是一个接口,我们看一下它的类图,它有一个唯一的实现类:RibbonLoadBalancerClient 。
		return this.loadBalancer.execute(serviceName,
				this.requestFactory.createRequest(request, body, execution));
	}

在这里插入图片描述

  • RibbonLoadBalancerClient.execute()
	public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
			throws IOException {
		//根据serviceId获得一个ILoadBalancer,实例为:ZoneAwareLoadBalancer
		ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
		//调用getServer方法去获取一个服务实例
		Server server = getServer(loadBalancer, hint);
		//判断Server的值是否为空。这里的Server实际上就是传统的一个服务节点,这个对象存储了服务节点的一些元数据,比如host、port、schema等
		if (server == null) {
			throw new IllegalStateException("No instances available for " + serviceId);
		}
		//封装成RibbonServer对象
		RibbonServer ribbonServer = new RibbonServer(serviceId, server,
				isSecure(server, serviceId),
				serverIntrospector(serviceId).getMetadata(server));

		return execute(serviceId, ribbonServer, request);
	}
  1. 根据serviceId获得一个ILoadBalancer,实例为ZoneAwareLoadBalancer
  2. 调用getServer方法去获取一个服务实例判断Server的值是否为空。这里的Server实际上就是传统的一个服务节点,这个对象存储了服务节点的一些元数据,比如host、port等
  3. 封装成RibbonServer对象
  4. 调用目标方法

ILoadBalancer是一个负载均衡接口,类的关系图如下:
在这里插入图片描述
此时具体的实现类我们可以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);
}

该类是一个配置类,可以看到如果我们没有自定义去实现ILoadBalancer接口,则直接返回一个ZoneAwareLoadBalancer。ZoneAwareLoadBalancer我们可以实现异地灾备。
ZoneAwareLoadBalancer的核心功能是

  1. 若开启了区域意识,且zone的个数 > 1,就继续区域选择逻辑
  2. 根据ZoneAvoidanceRule.getAvailableZones()方法拿到可用区们(会T除掉完全不可用的区域们,以及可用但是负载最高的一个区域)
  3. 从可用区zone们中,通过ZoneAvoidanceRule.randomChooseZone随机选一个zone出来(该随机遵从权重规则:谁的zone里面Server数量最多,被选中的概率越大)
  4. 在选中的zone里面的所有Server中,采用该zone对对应的Rule,进行choose
  • getServer(loadBalancer, hint)
    接着我们分析getServer(loadBalancer, hint)方法,通过上面的分析我们直到了此时,loadBalance实例对象是ZoneAwareLoadBalancer。
    @Override
    public Server chooseServer(Object key) {
    //ENABLED,表示是否用区域意识的choose选择Server,默认是true,
//如果禁用了区域、或者只有一个zone,就直接按照父类的逻辑来进行处理,父类默认采用轮询算法
        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 ,然后根据该区域的负载均衡算法选择一个server
                    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);
        }
    }

通过上面的代码分析我们直到如果没有配置多区域,那么会调用到super.chooseServer(key);方法,即BaseLoadBalancer.chooseServer方法

   public Server chooseServer(Object key) {
        if (counter == null) {
            counter = createCounter();
        }
        counter.increment();
        if (rule == null) {
            return null;
        } else {
            try {
            //根据默认的负载均衡算法来获得指定的服务节点。默认的算法是RoundBin。
                return rule.choose(key);
            } catch (Exception e) {
                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
                return null;
            }
        }
    }
  • rule.choose
    rule代表负载均衡算法规则,它有很多实现,IRule的实现类关系图如下。
    在这里插入图片描述
  1. BestAvailableRule:选择具有最低并发请求的服务器。
  2. ClientConfigEnabledRoundRobinRule:轮询。
  3. RandomRule:随机选择一个服务器。
  4. RoundRobinRule:轮询选择服务器。
  5. RetryRule:具备重试机制的轮询。
  6. WeightedResponseTimeRule:根据使用平均响应时间去分配一个weight(权重) ,weight越低,被选择的可能性就越低。
  7. ZoneAvoidanceRule:根据区域和可用性筛选,再轮询选择服务器。

默认情况下, rule 的实现为ZoneAvoidanceRule ,它是在RibbonClientConfiguration 这个配置类中定义的。

所以,在BaseLoadBalancer.chooseServer 中调用rule.choose(key); ,实际会进入到ZoneAvoidanceRule 的choose 方法,我们可以发现该类中并没有choose方法,但是该类继承了PredicateBasedRule说明会调用父类的方法:

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

    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())));
    }
    
public List<Server> getEligibleServers(List<Server> servers, Object loadBalancerKey) {
        List<Server> result = super.getEligibleServers(servers, loadBalancerKey);
        //按照fallbacks中存储的过滤器顺序进行过滤(此处就行先ZoneAvoidancePredicate然后AvailabilityPredicate)
        Iterator<AbstractServerPredicate> i = fallbacks.iterator();
        while (!(result.size() >= minimalFilteredServers && result.size() > (int) (servers.size() * minimalFilteredPercentage))
                && i.hasNext()) {
            AbstractServerPredicate predicate = i.next();
            result = predicate.getEligibleServers(servers, loadBalancerKey);
        }
        return result;
    }

getEligibleServers这个方法还是比较复杂的,执行过程很绕,建议debug执行一遍。
这里先说一下结论,他会通过AvailabilityPredicate, ZoneAvoidancePredicate这两个类进行筛选,

  1. AvailabilityPredicate,过滤熔断状态下的服务以及并发连接过多的服务。
  2. ZoneAvoidancePredicate,过滤掉无可用区域的节点。
  • CompositePredicate.getEligibleServers
@Override
public List<Server> getEligibleServers(List<Server> servers, Object loadBalancerKey) {
//
List<Server> result = super.getEligibleServers(servers, loadBalancerKey);
//按照fallbacks中存储的过滤器顺序进行过滤(此处就行先ZoneAvoidancePredicate然后
AvailabilityPredicateIterator<AbstractServerPredicate> i = fallbacks.iterator();
while (!(result.size() >= minimalFilteredServers && result.size() > (int)
(servers.size() * minimalFilteredPercentage))
&& i.hasNext()) {
AbstractServerPredicate predicate = i.next();
result = predicate.getEligibleServers(servers, loadBalancerKey);
}
return result;
}

依次使用次过滤条件对主过滤条件的结果进行过滤

  1. 不论是主过滤条件还是次过滤条件,都需要判断下面两个条件
  2. 只要有一个条件符合,就不再过滤,将当前结果返回供线性轮询
  • 第1个条件:过滤后的实例总数>=最小过滤实例数(默认为1)
  • 第2个条件:过滤互的实例比例>最小过滤百分比(默认为0)

AbstractServerPredicate.getEligibleServers

    public List<Server> getEligibleServers(List<Server> servers, Object loadBalancerKey) {
    //这里的实现逻辑是,遍历所有服务器列表,调用this.apply 方法进行验证,验证通过的节点,会加入到results 这个列表中返回。
        if (loadBalancerKey == null) {
            return ImmutableList.copyOf(Iterables.filter(servers, this.getServerOnlyPredicate()));            
        } else {
            List<Server> results = Lists.newArrayList();
            for (Server server: servers) {
            //this.apply ,会进入到CompositePredicate.apply
                if (this.apply(new PredicateKey(loadBalancerKey, server))) {
                    results.add(server);
                }
            }
            return results;            
        }
    }
@Override
public boolean apply(@Nullable PredicateKey input) {
//delegate 的实例是AbstractServerPredicate
	return delegate.apply(input);
}

  public static AbstractServerPredicate ofKeyPredicate(final Predicate<PredicateKey> p) {
        return new AbstractServerPredicate() {
            @Override
            @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "NP")
            public boolean apply(PredicateKey input) {
                return p.apply(input);
            }            
        };        
    }

也就是说,会通过AbstractServerPredicate.apply 方法进行过滤,其中, input 表示目
标服务器集群的某一个具体节点。
其中p ,表示AndPredicate 实例,这里用到了组合predicate进行判断,而这里的组合判断
是and的关系,用到了AndPredicate实现。

上述代码中,components是由两个predicate组合而成

  1. AvailabilityPredicate,过滤熔断状态下的服务以及并发连接过多的服务。
  2. ZoneAvoidancePredicate,过滤掉无可用区域的节点。
3.4 服务列表的加载过程

在本实例中,我们将服务列表配置在application.properties文件中,意味着在某个时候会加载这个列表,保存在某个位置,那它是在什么时候加载的呢?

在RibbonClientConfiguration这个配置类中,有下面这个Bean的声明,(该Bean是条件触发)它用来定义默认的负载均衡实现。

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

在改配置类中我们可以看到会创建一个区域负载均衡器ZoneAwareLoadBalancer,那么我们看下它的构造方法:

    public ZoneAwareLoadBalancer(IClientConfig clientConfig, IRule rule,
                                 IPing ping, ServerList<T> serverList, ServerListFilter<T> filter,
                                 ServerListUpdater serverListUpdater) {
         //调用父类的方法DynamicServerListLoadBalancer类
        super(clientConfig, rule, ping, serverList, filter, 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);
    }
  • restOfInit
    restOfInit方法主要做两件事情。
  1. 开启动态更新Server的功能
  2. 更新Server列表
    void restOfInit(IClientConfig clientConfig) {
        boolean primeConnection = this.isEnablePrimingConnections();
        // turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
        this.setEnablePrimingConnections(false);
        //开启动态更新Server(开启定时任务动态更新)
        enableAndInitLearnNewServersFeature();
		//更新Server列表
        updateListOfServers();
        if (primeConnection && this.getPrimeConnections() != null) {
            this.getPrimeConnections()
                    .primeConnections(getReachableServers());
        }
        this.setEnablePrimingConnections(primeConnection);
        LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
    }
  • updateListOfServers
    @VisibleForTesting
    public void updateListOfServers() {
        List<T> servers = new ArrayList<T>();
        if (serverListImpl != null) {
        //serverListImpl的实例为:ConfigurationBasedServerList,
        //调用getUpdatedListOfServers方法时,返回的是在application.properties文件中定义的服务列表。
            servers = serverListImpl.getUpdatedListOfServers();
            LOGGER.debug("List of Servers for {} obtained from Discovery client: {}",
                    getIdentifier(), servers);
			//判断是否需要filter,如果有,则通过filter进行服务列表过滤
            if (filter != null) {
                servers = filter.getFilteredListOfServers(servers);
                LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}",
                        getIdentifier(), servers);
            }
        }
        //更新所有Server到本地缓存中
        updateAllServerList(servers);
    }
3.5动态Ping机制

在Ribbon中,基于Ping机制,目标服务地址也会发生动态变更,具体的实现方式在DynamicServerListLoadBalancer.restOfInit方法中,我们这里再贴一下源码:

```java
    void restOfInit(IClientConfig clientConfig) {
        boolean primeConnection = this.isEnablePrimingConnections();
        // turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
        this.setEnablePrimingConnections(false);
        //开启动态更新Server(开启定时任务动态更新)
        enableAndInitLearnNewServersFeature();
		//更新Server列表
        updateListOfServers();
        if (primeConnection && this.getPrimeConnections() != null) {
            this.getPrimeConnections()
                    .primeConnections(getReachableServers());
        }
        this.setEnablePrimingConnections(primeConnection);
        LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
    }
  • enableAndInitLearnNewServersFeature

//注意,这里会启动一个定时任务,而定时任务所执行的程序是updateAction,它是一个匿名内部类,定义如下。
   public void enableAndInitLearnNewServersFeature() {
        LOGGER.info("Using serverListUpdater {}", serverListUpdater.getClass().getSimpleName());
        serverListUpdater.start(updateAction);
    }


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

//定时任务的启动方法如下,这个任务每隔30s执行一次
    @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");
        }
    

当30s之后触发了doUpdate方法后,最终进入到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);
            }
        }
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值