看spring cloud源碼分析好繞,還是堅持看完了。
先總結一下Ribbon的運行流程薛闪,可以跳過總結看下面迁霎,然后重新看總結吱抚。
- 項目啟動的時候會自動的為我們加載
LoadBalancerAutoConfiguration
自動配置類,該自動配置類初始化條件是要求classpath必須要有RestTemplate
這個類考廉,必須要有LoadBalancerClient
實現(xiàn)類秘豹。LoadBalancerAutoConfiguration
為我們干了二件事,第一件是創(chuàng)建了LoadBalancerInterceptor
攔截器bean昌粤,用于實現(xiàn)對客戶端發(fā)起請求時進行攔截既绕,以實現(xiàn)客戶端負載均衡。創(chuàng)建了一個
RestTemplateCustomizer
的bean涮坐,用于給RestTemplate
增加LoadBalancerInterceptor
攔截器凄贩。- 每次請求的時候都會執(zhí)行
org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor
的intercept
方法,而LoadBalancerInterceptor
具有LoadBalancerClient
(客戶端負載客戶端)實例的一個引用袱讹,
在攔截器中通過方法獲取服務名的請求url(比如http://user-service/user
)疲扎,及服務名(比如user-service),然后調(diào)用負載均衡客戶端的execute方法。- 執(zhí)行負載客戶端
RibbonLoadBalancerClient
(LoadBalancerClient的實現(xiàn))的execute
方法评肆,得到ILoadBalancer
(負載均衡器)的實現(xiàn)ZoneAwareLoadBalancer
债查,并且通過調(diào)用其chooseServer
方法獲得服務列表中的一個實例,比如說user-service列表注冊到eureka中一個實例瓜挽。然后向其中的一個具體實例發(fā)起請求盹廷,得到結果。
源碼分析
之前我們實現(xiàn)負載均衡是在消費端的RestTemplate
加上注解@LoadBalanced
久橙,便可以實現(xiàn)負載均衡了
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
查看注解內(nèi)容:
/**
* Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient
* @author Spencer Gibb
*/
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
這個注解給RestTemplate
做標記俄占,標記為LoadBalancerClient
。
查看LoadBalancerClient
源碼:
/**
* Represents a client side load balancer
* @author Spencer Gibb
*/
public interface LoadBalancerClient extends ServiceInstanceChooser {
/**
* 通過LoadBalancer的ServiceInstance對指定的服務執(zhí)行請求操作
*/
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
<T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
/**
* 為系統(tǒng)構建一個合適的host:port形式的url淆衷。在分布式系統(tǒng)中缸榄,我們使用邏輯上的服務名稱作為host來構建URI
* (替代服務實例的host:port形式)進行請求,比如說myservice/path/to/service祝拯。
*/
URI reconstructURI(ServiceInstance instance, URI original);
}
繼承自接口ServiceInstanceChooser
:
/**
* Implemented by classes which use a load balancer to choose a server to
* send a request to.
*
* @author Ryan Baxter
*/
public interface ServiceInstanceChooser {
/**
* Choose a ServiceInstance from the LoadBalancer for the specified service
* @param serviceId the service id to look up the LoadBalancer
* @return a ServiceInstance that matches the serviceId
*/
ServiceInstance choose(String serviceId);
}
ServiceInstance choose(String serviceId):根據(jù)傳入的服務名serviceId甚带,從負載均衡器中挑選一個對應服務的實例。
T execute(String serviceId, LoadBalancerRequest<T> request):使用從負載均衡器中挑選出來的服務實例來執(zhí)行請求內(nèi)容佳头。
T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request):使用從負載均衡器中挑選出來的服務實例來執(zhí)行請求內(nèi)容鹰贵。
URI reconstructURI(ServiceInstance instance, URI original):為系統(tǒng)構建一個合適的host:port形式的url。在分布式系統(tǒng)中康嘉,我們使用邏輯上的服務名稱作為host來構建URI(替代服務實例的host:port形式)進行請求碉输,比如說myservice/path/to/service。
順著LoadBalancerClient
接口的所屬包org.springframework.cloud.client.loadbalancer
,我們對內(nèi)容進行整理亭珍,可以得到下面的關系:
LoadBalancerAutoConfiguration
為客戶端Ribbon負載均衡的自動化配置類敷钾,
@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 loadBalancedRestTemplateInitializer(
final List<RestTemplateCustomizer> customizers) {
return new SmartInitializingSingleton() {
@Override
public void afterSingletonsInstantiated() {
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
//通過調(diào)用RestTemplateCustomizer的實例來給需要的客戶端負載均衡的RestTemplate增加LoadBalancerInterceptor攔截器。
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 {
//創(chuàng)建了一個LoadBalancerInterceptor的bean肄梨,用于實現(xiàn)對客戶端發(fā)起請求時進行攔截阻荒,以實現(xiàn)客戶端負載均衡。
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
//創(chuàng)建了一個RestTemplateCustomizer的bean峭范,用于給RestTemplate增加LoadBalancerInterceptor攔截器财松。
return new RestTemplateCustomizer() {
@Override
public void customize(RestTemplate restTemplate) {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
}
};
}
}
@ConditionalOnClass(RestTemplate.class)
:當前項目的classpath路徑下有RestTemplate這個類。
@ConditionalOnBean(LoadBalancerClient.class)
:spring容器中必須有LoadBalancerClient的實現(xiàn)bean
該自動化配置主要完成了三件事
- 創(chuàng)建了一個
LoadBalancerInterceptor
的bean纱控,用于實現(xiàn)對客戶端發(fā)起請求時進行攔截辆毡,以實現(xiàn)客戶端負載均衡。 - 創(chuàng)建了一個
RestTemplateCustomizer
的bean甜害,用于給RestTemplate增加LoadBalancerInterceptor
攔截器舶掖。 - 維護了一個被
@LoadBalanced
注解修飾的RestTemplate
對象列表,并在這里進行維護尔店,通過調(diào)用RestTemplateCustomizer
的實例來給需要的客戶端負載均衡的RestTemplate
增加LoadBalancerInterceptor
攔截器眨攘。
看看LoadBalancerInterceptor
攔截器是如何讓一個普通的RestTemplate
變成負載均衡的:
LoadBalancerClient
是一個抽象的接口主慰,originalUri.getHost()
獲取到的是服務名,execute
函數(shù)去根據(jù)服務名來選擇實例并發(fā)起實際的請求鲫售。
org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient
是實現(xiàn)LoadBalancerClient
接口共螺,看其實現(xiàn):
execute方法,使用從負載均衡器中挑選出來的服務實例來執(zhí)行請求內(nèi)容情竹。
getServer方法:
去調(diào)用ILoadBalancer實例的chooseServer方法
認識一下com.netflix.loadbalancer.ILoadBalancer
接口:
ILoadBalancer負載均衡器
Interface that defines the operations for a software loadbalancer. A typical
loadbalancer minimally need a set of servers to loadbalance for, a method to
mark a particular server to be out of rotation and a call that will choose a
server from the existing list of server.
定義軟件負載平衡器操作的接口藐不。 一個典型的負載均衡器最低限度地需要一組服務器來負載平衡,一種方法標記一個特定的服務器秦效,以避免旋轉和蔥已有的服務列表中選擇一個實例進行調(diào)用雏蛮。
public interface ILoadBalancer {
//向負載均衡器中維護的實例列表增加服務實例
public void addServers(List<Server> newServers);
//從負載均衡器中挑選出一個具體的服務實例
public Server chooseServer(Object key);
//用來通知和標記負載均衡器中的某個具體實例已經(jīng)停止服務,不然負載均衡器在下一次獲取服務實例清單前都會認為服務實例均是正常服務的阱州。
public void markServerDown(Server server);
/**
* @deprecated 2016-01-20 This method is deprecated in favor of the
* cleaner {@link #getReachableServers} (equivalent to availableOnly=true)
* and {@link #getAllServers} API (equivalent to availableOnly=false).
*
* Get the current list of servers.
*
* @param availableOnly if true, only live and available servers should be returned
*/
@Deprecated
public List<Server> getServerList(boolean availableOnly);
//獲取當前正常服務的實例列表
public List<Server> getReachableServers();
//獲取所有已知的服務實例列表挑秉,包括正常服務和停止服務實例。
public List<Server> getAllServers();
}
com.netflix.loadbalancer.Server
對象定義是一個傳統(tǒng)的服務端節(jié)點苔货,在該類中存儲了服務節(jié)點的一些元數(shù)據(jù)信息犀概,包括host,post以及一些部署信息等。
com.netflix.loadbalancer.ILoadBalancer
接口的一些實現(xiàn)蒲赂,
springcloud整合Ribbon的時候選擇采用的是com.netflix.loadbalancer.ZoneAwareLoadBalancer
負載均衡器阱冶。調(diào)用它的chooseServer
方法。
回到org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient
的execute
方法滥嘴,
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
//從上面跟過來我們知道這邊的serviceId其實就是服務名
ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
//通過ZoneAwareLoadBalancer的chooseServer函數(shù)獲取了負載均衡策略分配的服務實例對象Server之后,將其包裝成RibbonServer(增加了服務名serverid至耻,是否需要使用Https等其他信息)
Server server = this.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
的實現(xiàn)com.netflix.loadbalancer.ZoneAwareLoadBalancer
若皱,將其包裝成RibbonServer
,調(diào)用ZoneAwareLoadBalancer
的chooseServer
函數(shù)尘颓。
回到org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient
的execute
方法走触,調(diào)用LoadBalancerRequest
的apply
方法,向一個實際的具體服務實例發(fā)起請求疤苹,從而實現(xiàn)一開始以服務名為host的URI請求到host:port形式的實際訪問地址的轉換
apply
方法參數(shù)ServiceInstance
實例互广,ServiceInstance
類
上面說到的RibbonServer
對象就是ServiceInstance
接口的實現(xiàn)
我們已經(jīng)可以大概理清了Spring Cloud Ribbon中實現(xiàn)客戶端負載均衡的基本脈絡,了解它是如何通過LoadBalancerInterceptor
攔截器對RestTemplate
的請求進行攔截卧土,并利用Spring Cloud的負載均衡器LoadBalancerClient
將以邏輯服務名為host的URI轉換成具體的服務實例的過程惫皱。同時通過分析LoadBalancerClient
的Ribbon實現(xiàn)RibbonLoadBalancerClient
,可以知道在使用Ribbon實現(xiàn)負載均衡器的實現(xiàn)尤莺,實際使用的還是Ribbon中定義的ILoadBalancer接口的實現(xiàn)旅敷,自動化配置會采用ZoneAwareLoadBalancer
的實例來實現(xiàn)客戶端負載均衡。