Ribbon源码解析
Ribbon的使用流程图
- 从上图我们可以看到我们首先创建了一个Eureka集群,端口号分别为8001和8002
- User1服务和User2服务作为服务的提供方将自己的服务信息注册到Eureka中
- 此时Order服务作为服务的消费者,我们会首先从Eureka中拉取服务注册列表,放到本地,做为缓存
- ribbon拉取的服务注册列表则是Order本都服务的注册列表,而不是自己从Eureka中拉取的服务
- 我们在Order服务中使用resrTemplate.getForObject等方法调用,在调用之前会经过一个拦截器,该拦截器会将我们的参数中服务名字替换为具体的IP+端口的形式(拦截器是LoadBalancerInterceptor,RibbonLoadBalancerClient会将user-service替换为192.168.0.1:8002)
- 我们得到真实服务的http连接和端口,用此URL进行真实的调动。
- 注意Ribbon有一个Ping机制,该机制的作用是:如果通过ribbon多次调用服务的提供方,多次调用失败之后,会自动去除Ribbon的服务列表,那么再次访问的时候,就不会在访问这个失败的url
Ribbon的源码简介
- 我们在使用RestTemplate的时候,会使用@LoadBalance注解标识,我们找到@LoadBalance的注解所在的包,然后去同包目录下面找XXXXConfiguration类。因为这种类,我们会由SpringBoot自动进行配置。
- 我们发现在@LoadBalance注解的同包下面类LoadBalancerAutoConfiguration类,我们点进去观察
/*
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.client.loadbalancer;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.retry.backoff.BackOffPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Auto configuration for Ribbon (client side load balancing).
*
* @author Spencer Gibb
* @author Dave Syer
* @author Will Tran
* @author Gang Li
*/
@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
return () -> restTemplateCustomizers.ifAvailable(customizers -> {
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
});
}
@Autowired(required = false)
private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
@Bean
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory(
LoadBalancerClient loadBalancerClient) {
return new LoadBalancerRequestFactory(loadBalancerClient, transformers);
}
@Configuration
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
@Configuration
@ConditionalOnClass(RetryTemplate.class)
public static class RetryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public LoadBalancedRetryFactory loadBalancedRetryFactory() {
return new LoadBalancedRetryFactory() {};
}
}
@Configuration
@ConditionalOnClass(RetryTemplate.class)
public static class RetryInterceptorAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public RetryLoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties,
LoadBalancerRequestFactory requestFactory,
LoadBalancedRetryFactory loadBalancedRetryFactory) {
return new RetryLoadBalancerInterceptor(loadBalancerClient, properties,
requestFactory, loadBalancedRetryFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
}
- LoadBalancerAutoConfiguration类下面主要是初始化了一堆bean,这里挑几个重要的看
(3.1)LoadBalancerInterceptor
- 该类的作用就是创建一个LoadBalancerInterceptor对象
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
(3.2)RestTemplateCustomizer
- 该类的作用是返回一个RestTemplateCustomizer类型的定制器
- 我们将给所有的RestTemplate中加入一个LoadBalancerInterceptor拦截器(该拦截器就是我们上面初始化的那个拦截器)
- 会在后面的SmartInitializingSingleton中调用执行这个方法。
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
// lambda表达式
return restTemplate -> {
// 遍历所有的restTemplate,将RestTemplate的所有拦截器放到list中
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
// 将该list加上我们最新的刚刚创建的LoadBalancerInterceptor拦截器
list.add(loadBalancerInterceptor);
// 将其设置到restTemplate中
restTemplate.setInterceptors(list);
};
}
(3.3)SmartInitializingSingleton
- 该方法的作用执行所有的定制器,遍历RestTemplates列表(这个列表是在LoadBalancerAutoConfiguration类中声明的,下面会对其进行介绍),该定制器的作用其实就是给每个RestTemplate中加入拦截器,这里就是我们刚刚创建的RestTemplateCustomizer的bean对象的执行。刚才的是声明RestTemplateCustomizer类。
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
// 刚刚的RestTemplateCustomizer restTemplateCustomizer方法的定制器当做参数被传递进来,作为定制器使用(这里的customizers其中一个就是restTemplateCustomizer定制器)
// 便利了所有的标有@LoadBalance注解的restTemplate
// 然后用该定制器定制这些restTemplate
// 该定制器就是给restTemplate里面加一个拦截器
return () -> restTemplateCustomizers.ifAvailable(customizers -> {
// 定制器的回调,我们将所有的定制器定制到restTemplate中
// 里面的LoadBalancerAutoConfiguration.this.restTemplates就是我们上面这个类定义的RestTemplate,上面注有@LoadBalance注解
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
// 定制我们的RestTemplate
// 实际上调用的就是我们的RestTemplateCustomizer restTemplateCustomizer方法
// 给每一个RestTemplate加一个拦截器
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
});
}
(3.4)private List restTemplates = Collections.emptyList();
- 这就是我们在SmartInitializingSingleton方法中的参数restTemplates
- 容器在启动的时候会扫描所有的带有@LoadBalance注解的restTemplate,将所有的restTemplate放到这个集合中。
- 我们回到新建LoadBalancerInterceptor的方法
- 这里我们可以看到其方法中有一个形参LoadBalancerClient loadBalancerClient,也就是说我们的LoadBalancerClient也是注入进来的,那么这个对象又是在哪里初始化的?
- LoadBalancerClient是一个接口,表示的是一个客户端的负载均衡器,所有的操作都是在这个对象中完成的。
(4.1)RibbonLoadBalancerClient
- 该类实现了LoadBalancerClient
- 我们找到该类同包下的XXXXAutoConfiguration,XXXConfiguration类由SpringBoot自动加载
(4.2)RibbonAutoConfiguration
- 看上面的截图我们可以看到这里面有一个@bean自动创建了LoadBalancerClient对象
- 再看类上面的注解@AutoConfigureBefore,@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
表示的是在加载LoadBalancerAutoConfiguration类之前,我们会先加载这个类,这个类中又有初始化LoadBalancerClient对象,所以我们在调用LoadBalancerAutoConfiguration的LoadBalancerInterceptor方法的时候,一切都说的请了
- 上面的代码我们已经分析的告一段落,现在回到我们的调用
- 我们平常的调用是用restTemplate进行调用的,若我们在调用的URL中写的是服务的名字,而不是具体的IP+端口号,则一定要在声明RestTemplate的上面加上@LoadBalance
/**
* 配置类,声明RestTemplate
* @author 92823
*/
@Configuration
public class MyConfig {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
--------
/**
* @author 92823
*/
@RestController
public class UserController {
@Autowired
private RestTemplate restTemplate;
@GetMapping("/queryUerInfoById/{id}")
public String queryUerInfoById(@PathVariable String id) {
ResponseEntity<String> forEntity = restTemplate.getForEntity("http://my-eureka-consumer-order-1/queryOrderById/" + id, String.class);
String body = forEntity.getBody();
return body;
}
}
- 我们在利用RestTemplate调用之前,会被拦截器进行拦截
- 该拦截器是LoadBalancerInterceptor.intercept()方法进行拦截
- 其中最主要的方法就是intercept中的execute方法
(7.1)LoadBalancerClient.execute()
- LoadBalancrtClient是一个抽象类,这里利用多太实现,其具体的实现类上面分析了,应该是RibbonLoadBalancerClient进行实现
@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
// 1. 根据serviceId获取一个负载均衡器
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
// 2. 利用当前的负载均衡器,依据其负载均衡算法选择一个server
Server server = getServer(loadBalancer);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
serviceId), serverIntrospector(serviceId).getMetadata(server));
return execute(serviceId, ribbonServer, request);
}
- ILoadBalancer loadBalancer = getLoadBalancer(serviceId);表示的是根据serviceId获取一个负载均衡器
- Server server = getServer(loadBalancer);表示的是利用当前的负载均衡器,依据其负载均衡算法,选择一个server
(7.1.1)getLoadBalancer
protected ILoadBalancer getLoadBalancer(String serviceId) {
return this.clientFactory.getLoadBalancer(serviceId);
}
- 我们的loadBalancer是在容器中获取到的,那么我们猜测一下,这里肯定是在某一个地方进行@Bean初始化的
- 一般情况下我们是要找到某个类初始化的地方,可以在使用该类对象的类统计目录找到相关的Configuration类里面
- 初始化ZoneAwareLoadBalancer
@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);
}
- 其中的ServerList是来自:
@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;
}
- 所以我们知道了ILoadBalancer loadBalancer = getLoadBalancer(serviceId);默认拿到的是ZoneAwareLoadBalancer
(7.1.2)Server server = getServer(loadBalancer);
- 该方法的主要作用就是根据传进来的负载均衡器,依据指定算法挑选一个Server
protected Server getServer(ILoadBalancer loadBalancer) {
if (loadBalancer == null) {
return null;
}
return loadBalancer.chooseServer("default"); // TODO: better handling of key
}
------------------// LoadBalancer是ZoneAwareLoadBalancer实现的,所以具体的choose访问的是ZoneAwareLoadBalancer类中的
@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);
}
}
- 默认我们走的就是return super.chooseServer(key);这个方法
- 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;
}
}
}
- return rule.choose(key);我们默认选择的就是轮训算法:
- RoundRobinRule的choose方法
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
log.warn("no load balancer");
return null;
}
Server server = null;
int count = 0;
while (server == null && count++ < 10) {
List<Server> reachableServers = lb.getReachableServers();
// 得到所有的服务列表
List<Server> allServers = lb.getAllServers();
int upCount = reachableServers.size();
int serverCount = allServers.size();
if ((upCount == 0) || (serverCount == 0)) {
log.warn("No up servers available from load balancer: " + lb);
return null;
}
int nextServerIndex = incrementAndGetModulo(serverCount);
server = allServers.get(nextServerIndex);
if (server == null) {
/* Transient. */
Thread.yield();
continue;
}
if (server.isAlive() && (server.isReadyToServe())) {
return (server);
}
// Next.
server = null;
}
if (count >= 10) {
log.warn("No available alive servers after 10 tries from load balancer: "
+ lb);
}
return server;
}
- 我们最终的两个列表就是allServerList和upServerList
@Monitor(name = PREFIX + "AllServerList", type = DataSourceType.INFORMATIONAL)
protected volatile List<Server> allServerList = Collections
.synchronizedList(new ArrayList<Server>());
@Monitor(name = PREFIX + "UpServerList", type = DataSourceType.INFORMATIONAL)
protected volatile List<Server> upServerList = Collections
.synchronizedList(new ArrayList<Server>());
(8.1)RibbonClientConfiguration
- 这里我们继续看下这个类
- 我们在这个类中实现了PollingServerListUpdater将其作为bean注入到容器中,并且在IloadBalancer对象的生成的时候,将其作为参数传递
- 我们的PollingServerListUpdater实现了ServerListUpdater。
(8.2)DynamicServerListLoadBalancer
- 该类继承了BaseLoadBalancer,初始化ZoneAwareLoadBalancer的时候,实际上是初始化DynamicServerListLoadBalancer类
public ZoneAwareLoadBalancer(IClientConfig clientConfig, IRule rule,
IPing ping, ServerList<T> serverList, ServerListFilter<T> filter,
ServerListUpdater serverListUpdater) {
// 初始化的实际上就是DynamicServerListLoadBalancer
super(clientConfig, rule, ping, serverList, filter, serverListUpdater);
}
- 进入其构造方法
- 这里最重要的就是restOfInit(clientConfig);方法
(8.3)restOfInit(clientConfig);
void restOfInit(IClientConfig clientConfig) {
boolean primeConnection = this.isEnablePrimingConnections();
// turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
this.setEnablePrimingConnections(false);
// ribbon定时更新eureka实例列表
enableAndInitLearnNewServersFeature();
// 获取所有的eureka实例列表
updateListOfServers();
if (primeConnection && this.getPrimeConnections() != null) {
this.getPrimeConnections()
.primeConnections(getReachableServers());
}
this.setEnablePrimingConnections(primeConnection);
LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
}
(8.4)enableAndInitLearnNewServersFeature();
- 该方法的作用是定时更新Eureka的实例列表
/**
* 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);
}
-------这个start方法走的是PollingServerListUpdater的
@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 {
// 更新列表,其实调用的是DynamicServerListLoadBalancer.this.updateListOfServers();
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");
}
}
----- 上面的updateAction.doUpdate()
public void doUpdate() {
DynamicServerListLoadBalancer.this.updateListOfServers();
}
- 这不就又回来了么,restOfInit方法中我们就有一个步骤是获取所有的Eureka实例表updateListOfServers()
(8.5)updateListOfServers();
@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);
}
- 该方法的作用就是更新服务列表
(8.5.1)servers = serverListImpl.getUpdatedListOfServers();
// DiscoveryEnabledNIWSServerList类中的方法
@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中拿服务注册列表的)
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
// 从eurekaClient里面获取本地实例的缓存
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;
}
-------
/**
* Gets the list of instances matching the given VIP Address in the passed region.
*
* @param vipAddress - The VIP address to match the instances for.
* @param secure - true if it is a secure vip address, false otherwise
* @param region - region from which the instances are to be fetched. If <code>null</code> then local region is
* assumed.
*
* @return - The list of {@link InstanceInfo} objects matching the criteria, empty list if not instances found.
*/
@Override
public List<InstanceInfo> getInstancesByVipAddress(String vipAddress, boolean secure,
@Nullable String region) {
if (vipAddress == null) {
throw new IllegalArgumentException(
"Supplied VIP Address cannot be null");
}
Applications applications;
if (instanceRegionChecker.isLocalRegion(region)) {
applications = this.localRegionApps.get();
} else {
applications = remoteRegionVsApps.get(region);
if (null == applications) {
logger.debug("No applications are defined for region {}, so returning an empty instance list for vip "
+ "address {}.", region, vipAddress);
return Collections.emptyList();
}
}
if (!secure) {
return applications.getInstancesByVirtualHostName(vipAddress);
} else {
return applications.getInstancesBySecureVirtualHostName(vipAddress);
}
}
- 上面方法的调用链就是从Eureka Client中获取服务注册列表
(8.5.2)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);
}
}
}
--------
@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);
}
- 该方法的作用就是将服务实例列表设置到父亲的BaseLoadBalancer的allServerList中
(8.6)重新回到8.2
- DynamicServerListLoadBalancer类继承了BaseLoadBalancer类,调用方法initWithConfig
void initWithConfig(IClientConfig clientConfig, IRule rule, IPing ping) {
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")));
// 设置Server的定时Ping任务
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(new LoadBalancerStats(clientName));
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();
}
setPingInterval(pingIntervalTime); 这个方法的作用就是设置Server的定时Ping任务,IPing功能就是会定时刷新Ribbon的服务列表,将不可用的服务剔除,以致于负载均衡的时候不会让其一直访问错误的服务。
Feign源码解析
- 原理可以参考:
- OpenFeign原理
注意
- Feign利用了动态代理来实现,实际上底层还是Ribbon来实现的
- Ribbon是利用拦截器来实现的