springcloud ribbon 源碼分析(4)——spring cloud與ribbon整合時的默認ILoadBalancer是誰?

上篇說到會將RestTemplate方法的請求轉發給RibbonLoadBalancerClient.execute()方法去執行,本篇我們就來看看RibbonLoadBalancerClient.execute()方法是怎麼處理轉發過來的請求的。

本篇的重點是根據一個服務名,serviceId,類似ServiceA這樣的一個服務名稱,是如何找到對應的LoadBalancer的?我們默認使用的是哪個功能的LoadBalancer呢?

其實主要就是研究這行代碼: ILoadBalancer loadBalancer = getLoadBalancer(serviceId);

public class RibbonLoadBalancerClient implements LoadBalancerClient {

        @Override
	/**
	 * @param serviceId 就是服務名稱serviceName,比如ServiceA
	 */
	public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
		//通過這個服務名稱比如ServiceA,獲取了一個ILoadBalancer,ribbon的核心API和組件,負載均衡器,ILoadBalancer接口,就是ribbon的原生的接口
		ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
		//通過ILoadBalancer去對ServiceA對應的server list,通過自己的負載均衡的算法,選擇一個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);
	}

        protected ILoadBalancer getLoadBalancer(String serviceId) {
		return this.clientFactory.getLoadBalancer(serviceId);
	}

}

 

(1)通過SpringClientFactory來獲取對應的LoadBalancer

SpringClientFactory不是spring的包下的,是spring cloud與ribbon整合代碼的包下的:org.springframework.cloud.netflix.ribbon

這塊是對spring進行了一定程度上的封裝,封裝了一些東西,從spring裏面獲取bean的入口,都變成了這個spring cloud ribbon自己的SpringClientFactory

它的意思,就是說,對每個服務名稱,你要調用的每個服務,對應着服務名稱,都有一個對應的spring的ApplicationContext容器,ServiceA對應着一個自己的獨立的spring的ApplicationContext容器

比如說要獲取這個ServiceA服務的LoadBalancer,那麼就從ServiceA服務對應的自己的ApplicationContext容器中去獲取自己的LoadBalancer即可

如果是另外一個ServiceB服務,那麼又是另外的一個spring ApplicationContext,然後從裏面獲取到的LoadBalancer都是自己的容器裏的LoadBalancer

很明確了,在SpringClientFactory裏面,一個服務(比如說ServiceA) => 對應着一個獨立的ApplicationContext,裏面包含了自己這個服務的獨立的一堆的組件,比如說LoadBalancer。如果要獲取一個服務對應的LoadBalancer,其實就是在自己的那個ApplicationContext裏面去獲取那個LoadBalancer即可。根據ILoadBalancer接口類型,獲取一個ILoadBalancer接口類型的實例化的bean即可。

/**
 * A factory that creates client, load balancer and client configuration instances. It
 * creates a Spring ApplicationContext per client name, and extracts the beans that it
 * needs from there.
 *
 * 對每個服務名稱,你要調用的每個服務,對應着服務名稱,都有一個對應的spring的ApplicationContext容器,
 * 比如ServiceA對應着一個自己的獨立的spring的ApplicationContext容器
 *
 * @author Spencer Gibb
 * @author Dave Syer
 */
public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification> {

        /**
	 * Get the load balancer associated with the name.
	 * 獲取與名稱關聯的負載均衡器
	 * @throws RuntimeException if any error occurs
	 */
	public ILoadBalancer getLoadBalancer(String name) {
		return getInstance(name, ILoadBalancer.class);
	}

        @Override
	/**
	 * @param name 服務名稱,比如ServiceA
	 * @param type ILoadBalancer接口的class對象
	 */
	public <C> C getInstance(String name, Class<C> type) {
		//根據服務名稱和ILoadBalancer接口類型獲取一個ILoadBalancer實例
		C instance = super.getInstance(name, type);
		if (instance != null) {
			return instance;
		}
		IClientConfig config = getInstance(name, IClientConfig.class);
		return instantiateWithConfig(getContext(name), type, config);
	}
}

如果IDE提示說,需要spring-cloud-context相關的源碼,就將我們之前弄好的那個spring-cloud-commons那個源碼,粘貼進去即可,因爲spring-cloud-context就是在spring-cloud-commons下面的

 

(2)調用SpringClientFactory父類NamedContextFactory真正完成LoadBalancer的獲取

public abstract class NamedContextFactory<C extends NamedContextFactory.Specification>
		implements DisposableBean, ApplicationContextAware {

        /**
	 * 根據服務名稱和ILoadBalancer接口類型獲取一個ILoadBalancer實例
	 * @param name 服務名稱,比如ServiceA
	 * @param type	ILoadBalancer接口的class對象
	 * @param <T> 泛型
	 * @return ILoadBalancer實例
	 */
	public <T> T getInstance(String name, Class<T> type) {
		//根據ServiceA獲取對應的ApplicationContext
		AnnotationConfigApplicationContext context = getContext(name);
		if (BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context,
				type).length > 0) {
			//根據ILoadBalancer接口類型,在對應的ApplicationContext對象中獲取一個ILoadBalancer接口類型的實例化的bean
			return context.getBean(type);
		}
		return null;
	}

        /**
	 * 根據ServiceA獲取對應的ApplicationContext
	 * @param name 服務名稱,比如ServiceA
	 * @return	服務名稱ServiceA對應的ApplicationContext
	 */
	protected AnnotationConfigApplicationContext getContext(String name) {
		//如果Map中存在key=ServiceA對應的ApplicationContext就直接返回
                //否則就先新建一個key=ServiceA對應的ApplicationContext並保存,然後再返回新建的ApplicationContext
		if (!this.contexts.containsKey(name)) {
			synchronized (this.contexts) {
				if (!this.contexts.containsKey(name)) {
					this.contexts.put(name, createContext(name));
				}
			}
		}
		return this.contexts.get(name);
	}
}

 

(3)ILoadBalancer接口對象是怎麼實例化的?

如果你要找一個bean,要麼就在XXAutoConfiguration裏面找,要麼就是在XXConfiguration裏面找。。。spring cloud或者是spring boot的項目去找bean的一個特點。

RibbonClientConfiguration:在這個裏面可以找到對應的ILoadBalancer的實例bean

人家創建的就是:ZoneAwareLoadBalancer,這就是所謂的我們在spring cloud整合ribbon的環境下,使用的默認的LoadBalancer

ZoneAwareLoadBalancer的父類是:DynamicServerListLoadBalancer,他的父類又是:BaseLoadBalancer

public class RibbonClientConfiguration {

        @Bean
	@ConditionalOnMissingBean
	//創建一個ILoadBalancer類型的實例ZoneAwareLoadBalancer,並放入spring容器
	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);
		}
		//ZoneAwareLoadBalancer的父類是:DynamicServerListLoadBalancer,他的父類又是:BaseLoadBalancer
		return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
				serverListFilter, serverListUpdater);
	}
}

 

總結:獲取LoadBalancer的這個過程 流程圖

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章