上篇說到會將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的這個過程 流程圖