ribbon学习笔记(三)RibbonLoadBalancerClient的execute是怎么执行的?如何获得一个ILoadBalancer

一、先看下整体代码

public <T> T execute(String serviceId, LoadBalancerRequest<T> request)
		throws IOException {
	return execute(serviceId, request, null);
}

public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
			throws IOException {
	ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
	Server server = getServer(loadBalancer, hint);
	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负载均衡器,接着通过负载均衡器拿到了一个Server,其次把Server包装成了一个RibbonServer,最后在执行了一个execute方法。我们先来看看它是怎么获取一个负载均衡器的。我们先去看一下getLoadBalancer方法

二、如何获得一个ILoadBalancer

// 1. 承接上一步的: 
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);

// 2.org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient#getLoadBalancer
// 注意这里的clientFactory是SpringClientFactory
protected ILoadBalancer getLoadBalancer(String serviceId) {
	return this.clientFactory.getLoadBalancer(serviceId);
}

// 3.org.springframework.cloud.netflix.ribbon.SpringClientFactory#getLoadBalancer
public ILoadBalancer getLoadBalancer(String name) {
	return getInstance(name, ILoadBalancer.class);
}

// 4.org.springframework.cloud.netflix.ribbon.SpringClientFactory#getInstance
public <C> C getInstance(String name, Class<C> type) {
	C instance = super.getInstance(name, type);
	if (instance != null) {
		return instance;
	}
	IClientConfig config = getInstance(name, IClientConfig.class);
	return instantiateWithConfig(getContext(name), type, config);
}

// 5.org.springframework.cloud.context.named.NamedContextFactory#getInstance
public <T> T getInstance(String name, Class<T> type) {
	AnnotationConfigApplicationContext context = getContext(name);
	if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
			type).length > 0) {
		return context.getBean(type);
	}
	return null;
}

// 6.org.springframework.cloud.context.named.NamedContextFactory#getContext
protected AnnotationConfigApplicationContext getContext(String name) {
	if (!this.contexts.containsKey(name)) {
		synchronized (this.contexts) {
			if (!this.contexts.containsKey(name)) {
				this.contexts.put(name, createContext(name));
			}
		}
	}
	return this.contexts.get(name);
}

// 7.org.springframework.cloud.context.named.NamedContextFactory#createContext
protected AnnotationConfigApplicationContext createContext(String name) {
	AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
	if (this.configurations.containsKey(name)) {
		for (Class<?> configuration : this.configurations.get(name)
				.getConfiguration()) {
			context.register(configuration);
		}
	}
	for (Map.Entry<String, C> entry : this.configurations.entrySet()) {
		if (entry.getKey().startsWith("default.")) {
			for (Class<?> configuration : entry.getValue().getConfiguration()) {
				context.register(configuration);
			}
		}
	}
	context.register(PropertyPlaceholderAutoConfiguration.class,
			this.defaultConfigType);
	context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(
			this.propertySourceName,
			Collections.<String, Object>singletonMap(this.propertyName, name)));
	if (this.parent != null) {
		// Uses Environment from parent as well as beans
		context.setParent(this.parent);
		// jdk11 issue
		// https://github.com/spring-cloud/spring-cloud-netflix/issues/3101
		context.setClassLoader(this.parent.getClassLoader());
	}
	context.setDisplayName(generateDisplayName(name));
	context.refresh();
	return context;
}

    通过上面的调用链路可以发现,在第5步的时候,它通过服务名(ServerId)拿到了一个Spring容器,并且在第6步的时候判断了一下有没有对应的Spring容器,没有的话就创建一个,并保存到Map中,其中一个ServerId对应一个Spring容器。这里可以发现Ribbon与SpringCloud整合后,每个服务都是独立开来的,互不影响~
    接着从第4步中的Spring容器中拿到了一个IloadBalance实例,并且给他初始化了一些配置,然后返回回去。这里我们暂时不知道它到底从Spring容器中拿到了那个实现类,管他那么多,先看一下类图。
在这里插入图片描述
    可以看到是这么一个类结构,经过笔者不断的ALT+F7、结合Ribbon要从Eureka上面啦服务列表、断点调试等,最终发现了从Spring拿到的实现类是ZoneAwareLoadBalancer。并且找到了创建它的地方:
在这里插入图片描述

    并且找到了ZoneAwareLoadBalancer构造方法中几个参数(都在RibbonClientConfiguration类中):

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

    知道了ZoneAwareLoadBalancer如何创建以及构造函数的参数从何而来,是什么以后,我们来看看ZoneAwareLoadBalancer的构造方法吧~

// 1.调用父类的构造方法
public ZoneAwareLoadBalancer(IClientConfig clientConfig, IRule rule,
                                 IPing ping, ServerList<T> serverList, ServerListFilter<T> filter,
                                 ServerListUpdater serverListUpdater) {
    super(clientConfig, rule, ping, serverList, filter, serverListUpdater);
}

// 2.先调用父类的构造方法,然后本类设置一些属性,再调用restOfInit方法
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);
}

// 3.这里看到只是一些初始化属性配置,就不细看了
public BaseLoadBalancer(IClientConfig config, IRule rule, IPing ping) {
    initWithConfig(config, rule, ping, createLoadBalancerStatsFromConfig(config));
}

    看了一圈,好像都是一些配置的代码,那就来看看DynamicServerListLoadBalancer#restOfInit方法吧,笔者加了点注释,看注释就好。

// 1.com.netflix.loadbalancer.DynamicServerListLoadBalancer#restOfInit
void restOfInit(IClientConfig clientConfig) {
     boolean primeConnection = this.isEnablePrimingConnections();
     this.setEnablePrimingConnections(false);
     // 2.从服务端拉取Server注册列表
     enableAndInitLearnNewServersFeature();
     // 3.更新最新的Server注册列表,这里介绍吧List<Server>更新一下,就不进去看了
     updateListOfServers();
     if (primeConnection && this.getPrimeConnections() != null) {
         this.getPrimeConnections()
                 .primeConnections(getReachableServers());
     }
     this.setEnablePrimingConnections(primeConnection);
     LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
 }

// 2.1 com.netflix.loadbalancer.DynamicServerListLoadBalancer#enableAndInitLearnNewServersFeature
public void enableAndInitLearnNewServersFeature() {
   LOGGER.info("Using serverListUpdater {}", serverListUpdater.getClass().getSimpleName());
    serverListUpdater.start(updateAction);
}

// 2.2 com.netflix.loadbalancer.PollingServerListUpdater#start
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 {
                	// 从这里进去,并且是定时执行的!30秒一次,(PollingServerListUpdater.LISTOFSERVERS_CACHE_REPEAT_INTERVAL)
                    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");
    }
}

// 2.3 com.netflix.loadbalancer.DynamicServerListLoadBalancer#updateListOfServers
@VisibleForTesting
public void updateListOfServers() {
    List<T> servers = new ArrayList();
    if (this.serverListImpl != null) {
    	// 这里进去
        servers = this.serverListImpl.getUpdatedListOfServers();
        LOGGER.debug("List of Servers for {} obtained from Discovery client: {}", this.getIdentifier(), servers);
        if (this.filter != null) {
            servers = this.filter.getFilteredListOfServers((List)servers);
            LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}", this.getIdentifier(), servers);
        }
    }

    this.updateAllServerList((List)servers);
}

// 2.4 com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList#obtainServersViaDiscovery
// 这里就是全部的流程了,每隔30秒从Eureka上面获取服务注册列表
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 = createServer(ii, isSecure, shouldUseIpAddr);
                        serverList.add(des);
                    }
                }
                if (serverList.size()>0 && prioritizeVipAddressBasedServers){
                    break; // if the current vipAddress has servers, we dont use subsequent vipAddress based servers
                }
            }
        }
        return serverList;
    }

总结

    看到这里就知道Ribbon怎么和SpringCloud整合了,并且也知道了Ribbon怎么从Eureka上获取服务列表,以及每隔30秒去Eureka上拉取服务列表,下一篇看一下Ribbon怎么从一堆Server中选择一个。继续来个图:
在这里插入图片描述

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值