- SpringCloud 版本 :Hoxton.SR1
- SpringBoot 版本:2.2.1.RELEASE
- 本文適用于對(duì)SpringBoot有一定基礎(chǔ)得人,主要講解Eureka 客戶端得相關(guān)底層實(shí)現(xiàn),講解方式:
場景驅(qū)動(dòng)- 關(guān)鍵詞 :客戶端源碼分析、客戶端負(fù)載均衡源碼分析
- 上一篇 SpringCloud 服務(wù)注冊與發(fā)現(xiàn) 源碼分析(一) 分析了 Eureka Server 端的設(shè)計(jì),本篇將會(huì)介紹客戶端及負(fù)載均衡的相關(guān)設(shè)計(jì)。
文末會(huì)留有兩個(gè)思考題
2. Eureka Client
同服務(wù)端一樣,我們分析的入口也是spring.factories文件。如下面:

EurekaClientAutoConfiguration
-
此類類似于服務(wù)端的核心自動(dòng)裝配類
EurekaServerAutoConfiguration配置類,根據(jù)順序我們先來看一下頭部的注釋類:圖2 EurekaClientAutoConfiguration類的頭部注解類 - 要裝載此類,類路徑下需要有EurekaClientConfig類,此類就類似于我們服務(wù)端分析的EurekaServerConfig類。
- 緊接著又Imort了DiscoveryClientOptionalArgsConfiguration類:由于路徑下有ClientFilter類,所以此處會(huì)創(chuàng)建MutableDiscoveryClientOptionalArgs對(duì)象,用于添加額外的jersy過濾器參數(shù)設(shè)置圖3 DiscoveryClientOptionalArgsConfiguration配置類
- 當(dāng)eureka.client.enabled屬性為true時(shí),才會(huì)裝配當(dāng)前類,由于默認(rèn)為true,所以只要不顯式的設(shè)置為false都會(huì)裝配當(dāng)前類。
- @ConditionalOnDiscoveryEnabled:,當(dāng)spring.cloud.discovery.enabled參數(shù)為true時(shí)才會(huì)自動(dòng)裝配當(dāng)前類,由于默認(rèn)為true,只要不顯式設(shè)置為false都會(huì)裝配。圖4 服務(wù)發(fā)現(xiàn)啟動(dòng)注解
-
@AutoConfigureAfter(name = {
"org.springframework.cloud.autoconfigure.RefreshAutoConfiguration",
"org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration",
"org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration" }): 此處的作用是在這些類裝配之后再會(huì)去自動(dòng)裝配(相對(duì)順序)。
①RefreshAutoConfiguration:當(dāng)環(huán)境信息變化時(shí),刷新屬性。(例如某些配置參數(shù)刷新;日志級(jí)別切換等。)。其中創(chuàng)建的核心類包括:RefreshEventListener(1. 用于監(jiān)聽事件ApplicationReadyEvent(將上下文標(biāo)識(shí)改完true,表示已刷新)2.監(jiān)聽RefreshEvent事件,此處就是實(shí)現(xiàn)環(huán)境屬性配置刷新的核心,監(jiān)聽到此事件之后會(huì)觸發(fā)ContextRefresher類的refresh方法,而該刷新方法首先會(huì)判斷環(huán)境配置屬性是否有改變,有的話會(huì)發(fā)布一個(gè)EnvironmentChangeEvent事件,并將改變的keys集作為事件源,然后又會(huì)調(diào)用RefreshScope的refreshAll方法(銷毀所有的刷新實(shí)例,并發(fā)布RefreshScopeRefreshedEvent事件),上述即是屬性配置動(dòng)態(tài)刷新的實(shí)現(xiàn))、RefreshScope(當(dāng)監(jiān)聽到ContextRefreshedEvent上下文刷新完成事件時(shí),并且當(dāng)bean的scope屬性為refresh時(shí),會(huì)觸發(fā)早期初始化bean)
圖5 RefreshEventListener的刷新監(jiān)聽邏輯圖6 ContextRefresher的refresh方法實(shí)現(xiàn)
②EurekaDiscoveryClientConfiguration:此類也是依賴于EurekaClientConfig.class類和eureka.client.enabled屬性才會(huì)裝配。另外還有其他兩處作用:1. 若當(dāng)前沒有EurekaDiscoveryClient的bean會(huì)去創(chuàng)建一個(gè)實(shí)例,此類實(shí)現(xiàn)了DiscoveryClient(屬于spring-cloud-commons抽象下的類,此包很重要,后面會(huì)單獨(dú)一篇文章介紹,例如:Nacos中就實(shí)現(xiàn)了此抽象接口,com.alibaba.cloud.nacos.discovery.NacosDiscoveryClient),用于客戶端服務(wù)發(fā)現(xiàn)(可以從Eureka、consul等注冊中心中獲取服務(wù)實(shí)例)圖7 EurekaDiscoveryClient的核心實(shí)現(xiàn)
-
內(nèi)部靜態(tài)類 EurekaClientConfigurationRefresher 會(huì)監(jiān)聽RefreshScopeRefreshedEvent事件(該事件上面提到過,當(dāng)指定的bean刷新時(shí)會(huì)發(fā)布該事件),監(jiān)聽到之后會(huì)有兩步操作:監(jiān)聽到事件之后就強(qiáng)制調(diào)用獲取實(shí)例的方法; 服務(wù)自動(dòng)注冊也需要重啟。
圖8 EurekaClientConfigurationRefresher的邏輯處理
③AutoServiceRegistrationAutoConfiguration:服務(wù)自動(dòng)注冊裝配類,初始化的時(shí)候會(huì)去檢查是否有自動(dòng)服務(wù)注冊AutoServiceRegistration的bean(該bean的創(chuàng)建過程也是在EurekaClientAutoConfiguration中),還會(huì)Import一個(gè)AutoServiceRegistrationConfiguration配置類:該類會(huì)啟動(dòng)創(chuàng)建一個(gè)服務(wù)自動(dòng)注冊屬性配置類,將圖9 AutoServiceRegistrationConfiguration環(huán)境中spring.cloud.service-registry.auto-registration開頭的屬性綁定到當(dāng)前類中。:圖10 AutoServiceRegistrationProperties
-
分析完@AutoConfigureAfter之后,我們看一下當(dāng)前裝配類都創(chuàng)建了哪些重要的組件:圖11 EurekaClientAutoConfiguration自動(dòng)裝配類的關(guān)鍵代碼圖12 EurekaClientAutoConfiguration自動(dòng)裝配類的關(guān)鍵代碼
①是通過構(gòu)造器注入當(dāng)前上下文
②是如果當(dāng)前容器中不存在EurekaClientConfig將會(huì)創(chuàng)建一個(gè)EurekaClientConfigBean實(shí)現(xiàn),而且當(dāng)環(huán)境中的 spring.config.name屬性值為 bootstrap時(shí),當(dāng)前客戶端實(shí)例不注冊到Eureka中(即默認(rèn)啟動(dòng)的時(shí)候不注冊,但是后面還會(huì)有一次注冊的機(jī)會(huì))
③是若容器中不存在EurekaInstanceConfig將會(huì)創(chuàng)建一個(gè)EurekaInstanceConfigBean實(shí)現(xiàn),用于保存服務(wù)端的相關(guān)配置,可以供其他服務(wù)查閱或者服務(wù)之間互相發(fā)現(xiàn)。
④是創(chuàng)建一個(gè) EurekaServiceRegistry注冊器Bean實(shí)例,該類實(shí)現(xiàn)了ServiceRegistry(spring-cloud-commons下的服務(wù)注冊抽象,此外Nacos中也對(duì)該抽象進(jìn)行了相應(yīng)得擴(kuò)展:com.alibaba.cloud.nacos.registry.NacosServiceRegistry,感興趣的讀者可以看看SpringCloudAlibaba的源碼)
⑤由于前面分析的會(huì)創(chuàng)建AutoServiceRegistrationProperties的屬性配置類,所以@ConditionalOnBean(AutoServiceRegistrationProperties.class)是滿足的,然后會(huì)通過構(gòu)造器注入到當(dāng)前實(shí)例中:

該類實(shí)現(xiàn)了
AutoServiceRegistration(也是spring-cloud-commons下的抽象,用于保存注冊得元數(shù)據(jù),例如 Nacos中也有相應(yīng)的實(shí)現(xiàn):com.alibaba.cloud.nacos.registry.NacosAutoServiceRegistration),還同時(shí)實(shí)現(xiàn)了 SmartLifecycle生命周期類,容器啟動(dòng)之后會(huì)通過生命周期回調(diào)start方法,注冊實(shí)例(本質(zhì)是將實(shí)力狀態(tài)改為上線UP),將運(yùn)行狀態(tài)改為false并發(fā)布InstanceRegisteredEvent事件;容器關(guān)閉會(huì)回調(diào)stop方法,方法會(huì)調(diào)用注冊器的deregister方法,將實(shí)例狀態(tài)改為下線DOWN,并將運(yùn)行狀態(tài)改為false。
⑥內(nèi)部靜態(tài)類RefreshableEurekaClientConfiguration,當(dāng)路徑下有RefreshScope并且容器中有RefreshAutoConfiguration時(shí),會(huì)自動(dòng)裝配當(dāng)前類,看名字就可以感覺到當(dāng)前配置類可以實(shí)現(xiàn)屬性配置刷新機(jī)制。
⑦創(chuàng)建EurekaClient實(shí)例,服務(wù)發(fā)現(xiàn)、獲取實(shí)例的底層依賴(Eureka原生)。由于默認(rèn)情況下spring容器會(huì)為對(duì)象生成一個(gè)代理對(duì)象,此處有一個(gè)使用目標(biāo)對(duì)象的轉(zhuǎn)化邏輯,最后生成CloudEurekaClient實(shí)例(springcloud的服務(wù)發(fā)現(xiàn)的實(shí)現(xiàn),繼承了com.netflix.discovery.DiscoveryClient類,DiscoveryClient類里實(shí)現(xiàn)具體的服務(wù)發(fā)現(xiàn)與相關(guān)邏輯。)
⑧當(dāng)容器中不存在ApplicationInfoManager實(shí)例bean時(shí),會(huì)創(chuàng)建一個(gè),此類用來管理實(shí)例信息與實(shí)例配置(服務(wù)端也創(chuàng)建了當(dāng)前實(shí)例管理器)
⑨創(chuàng)建一個(gè)EurekaRegistration實(shí)例,該實(shí)例實(shí)現(xiàn)了Registration(此接口僅代表一個(gè)可以注冊的標(biāo)志:服務(wù)元數(shù)據(jù)),而Registration又繼承自ServiceInstance ( spring-cloud-commons抽象接口,表示一個(gè)服務(wù)實(shí)例), 到此核心裝配類EurekaClientAutoConfiguration基本分析完畢!
3. 負(fù)載均衡 Ribbon
- 負(fù)載均衡是分布式應(yīng)用中重要的一個(gè)環(huán)節(jié),將服務(wù)提供分?jǐn)偟讲煌?jié)點(diǎn)以實(shí)現(xiàn)更高的負(fù)載能力。負(fù)載均衡通常分為硬負(fù)載和軟負(fù)載,軟負(fù)載又分為客戶端負(fù)載均衡和服務(wù)端負(fù)載均衡。 提到客戶端就不得不提客戶端負(fù)載均衡
Ribbon,Ribbon 屬于一個(gè)客戶端負(fù)載均衡框架。
RibbonAutoConfiguration
-
配置類的頭部信息:從上到下依次為:圖14 RibbonAutoConfiguration
①@RibbonClients:圖15 RibbonClients注解
一個(gè)Configuration配置類,標(biāo)注有@Import(RibbonClientConfigurationRegistrar.class),而RibbonClientConfigurationRegistrar注冊器中會(huì)創(chuàng)建RibbonClientConfigurationRegistrar類的bean定義,此類中會(huì)保存客戶端配置實(shí)例(defaultConfiguration參數(shù)指定的配置)。
②@AutoConfigureAfter(name="EurekaClientAutoConfiguration"):指定在EurekaClientAutoConfiguration之后裝配(也可以看出Ribboon是基于客戶端)
③@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,AsyncLoadBalancerAutoConfiguration.class }):指定在LoadBalancerAutoConfiguration和AsyncLoadBalancerAutoConfiguration之前裝配。
④@EnableConfigurationProperties({ RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class }):生成屬性配置bean,并綁定屬性 -
配置類的成員變量:圖16 成員變量
① 注入客戶端配置類集合
② 注入RibbonEagerLoadProperties - 配置類創(chuàng)建Bean,如下:
public class RibbonAutoConfiguration {
// 1.創(chuàng)建客戶端、負(fù)載均衡、客戶端配置實(shí)例的工廠,其中this.configurations就是@RibbonClients主鍵中指定的配置參數(shù)
// 2. 另外無參構(gòu)造器中會(huì)調(diào)用 super(RibbonClientConfiguration.class, NAMESPACE, "ribbon.client.name");
// 設(shè)置默認(rèn)的配置類型字段defaultConfigType為RibbonClientConfiguration.class,此處的字段作用是創(chuàng)建上下文時(shí)會(huì)為每個(gè)資源創(chuàng)建一個(gè)RibbonClientConfiguration類型的bean,且會(huì)將屬性名稱添加到環(huán)境中。
@Bean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations);
return factory;
}
// 負(fù)載均衡客戶端,Eureka的實(shí)現(xiàn)是 RibbonLoadBalancerClient,
// 包括重組URL方法( reconstructURI() )、選擇服務(wù)實(shí)例方法( choose() )、選擇實(shí)例之后的執(zhí)行方法( execute() )
// getServer()方法( 會(huì)調(diào)用對(duì)應(yīng)ILoadBalancer實(shí)現(xiàn)的chooseServer()方法)
@Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(springClientFactory());
}
// 創(chuàng)建重試策略 RibbonLoadBalancedRetryPolicy
@Bean
@ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
@ConditionalOnMissingBean
public LoadBalancedRetryFactory loadBalancedRetryPolicyFactory(
final SpringClientFactory clientFactory) {
return new RibbonLoadBalancedRetryFactory(clientFactory);
}
// 創(chuàng)建屬性工廠Bean,類中回初始化一個(gè)Map結(jié)合用于存放一些類與屬性的映射關(guān)系
// 例如鍵為Iping.class,值為NFLoadBalancerRuleClassName的映射(Iping是用來檢查服務(wù)端是否存活)。
@Bean
@ConditionalOnMissingBean
public PropertiesFactory propertiesFactory() {
return new PropertiesFactory();
}
}
- 由于RibbonAutoConfiguration是在
LoadBalancerAutoConfiguration配置類和AsyncLoadBalancerAutoConfiguration配置類之前先解析,所以接下來我依次分析這兩個(gè)配置類,并且這兩個(gè)配置類也是負(fù)載均衡的核心配置類。
LoadBalancerAutoConfiguration
正如名字一樣,負(fù)載均衡自動(dòng)配置類。
-
類的頭部信息:
圖17 LoadBalanceAutoConfiguration的頭部注釋信息
① 由于負(fù)載均衡的配置是針對(duì)于RestTemplate的,所有路徑下一定要有RestTemplate。
② 容器中必須要存在LoadBalancerClient的實(shí)現(xiàn)才會(huì)裝配
③ 啟動(dòng)創(chuàng)建LoadBalancerRetryProperties的配置Bean -
類中的屬性:
圖18 LoadBalancerAutoConfiguration的屬性信息
① 帶有負(fù)載均衡的(帶有@LoadBalanced注解)RestTemplate的集合(重點(diǎn),容器中帶有@LoadBalanced注解的RestTemplate都會(huì)注入到此集合中)
② 帶有可以修改HttpRequest對(duì)象的轉(zhuǎn)換器實(shí)現(xiàn)集合(根據(jù)被給的服務(wù)實(shí)例ServiceInstance去自定義修改指定的HttpRequest對(duì)象) 類的Bean創(chuàng)建邏輯:
public class LoadBalancerAutoConfiguration {
// 對(duì)實(shí)例化之后的bean進(jìn)行后置處理
//(正常情況下,需要實(shí)現(xiàn)SmartInitializingSingleton的afterSingletonsInstantiated方法,
// 可以對(duì)創(chuàng)建好的bean進(jìn)行處理,此處也是同樣的邏輯),
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
return () -> restTemplateCustomizers.ifAvailable(customizers -> {
// 遍歷所有的RestTemplate實(shí)例
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
// 循環(huán)執(zhí)行自定義方法,修改RestTemplate實(shí)例
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
});
}
/**
* Auto configuration for retry intercepting mechanism.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RetryTemplate.class)
public static class RetryInterceptorAutoConfiguration {
// 創(chuàng)建重試的負(fù)載均衡攔截器實(shí)例
@Bean
@ConditionalOnMissingBean
public RetryLoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRetryProperties properties,
LoadBalancerRequestFactory requestFactory,
LoadBalancedRetryFactory loadBalancedRetryFactory) {
return new RetryLoadBalancerInterceptor(loadBalancerClient, properties,
requestFactory, loadBalancedRetryFactory);
}
// 創(chuàng)建自定義的 RestTemplateCustomizer實(shí)現(xiàn)
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
// 獲取每個(gè)RestTemplate的所有攔截器
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
// 添加負(fù)載均衡攔截器( 此處是重試負(fù)載均衡攔截器 )
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
}
AsyncLoadBalancerAutoConfiguration
-
類的頭部信息:圖19 AsyncLoadBalancerAutoConfiguration的頭部信息
① 當(dāng)負(fù)載均衡客戶端實(shí)例存在時(shí)才裝配(因?yàn)閿r截器中會(huì)注入攔截器客戶端實(shí)例)
② 當(dāng)路徑中存在AsyncRestTemplate類時(shí)才裝配(因?yàn)樵撟詣?dòng)裝配類是針對(duì)于異步調(diào)用模板類AsyncRestTemplate的) -
類中的屬性:圖20 異步負(fù)載均衡配置類AsyncLoadBalancerAutoConfiguration的屬性
① 此處的注入方式跟前面提到的RestTemplate如出一轍,無論從使用方式還是配置方式兩者都神似( 簡直就是一模一樣 ),唯一的區(qū)別就是AsyncRestTemplate調(diào)用模板返回的結(jié)果會(huì)使用ListenableFuture接口(此處Spring作者說是靈感來自Google的ListenableFuture接口)包裝一下,而ListenableFuture接口繼承自java.util.concurrent.Future,看到這里我們就可以知道異步調(diào)用的原因了。 - 類的Bean創(chuàng)建邏輯:
public class AsyncLoadBalancerAutoConfiguration {
@Configuration(proxyBeanMethods = false)
static class AsyncRestTemplateCustomizerConfig {
// 會(huì)注入帶有@LoadBalanced注入的AsyncRestTemplate實(shí)例到當(dāng)前集合中
@LoadBalanced
@Autowired(required = false)
private List<AsyncRestTemplate> restTemplates = Collections.emptyList();
@Bean
public SmartInitializingSingleton loadBalancedAsyncRestTemplateInitializer(
final List<AsyncRestTemplateCustomizer> customizers) {
return new SmartInitializingSingleton() {
@Override
public void afterSingletonsInstantiated() {
// 遍歷配置的所有異步模板類
for (AsyncRestTemplate restTemplate : AsyncRestTemplateCustomizerConfig.this.restTemplates) {
for (AsyncRestTemplateCustomizer customizer : customizers) {
// 調(diào)用自定義方法修改模板類
customizer.customize(restTemplate);
}
}
}
};
}
}
@Configuration(proxyBeanMethods = false)
static class LoadBalancerInterceptorConfig {
// 創(chuàng)建異步負(fù)載均衡攔截器(并注入負(fù)載均衡客戶端)
@Bean
public AsyncLoadBalancerInterceptor asyncLoadBalancerInterceptor(
LoadBalancerClient loadBalancerClient) {
return new AsyncLoadBalancerInterceptor(loadBalancerClient);
}
// 創(chuàng)建異步調(diào)用模板類自定義實(shí)現(xiàn)類
@Bean
public AsyncRestTemplateCustomizer asyncRestTemplateCustomizer(
final AsyncLoadBalancerInterceptor loadBalancerInterceptor) {
return new AsyncRestTemplateCustomizer() {
@Override
public void customize(AsyncRestTemplate restTemplate) {
// 獲取異步調(diào)用模板類中攔截器列表
List<AsyncClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
// 添加異步負(fù)載均衡調(diào)用攔截器
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
}
};
}
}
}
RibbonEurekaAutoConfiguration
此類存在于spring.factories中,表示如果Eureka客戶端啟動(dòng)時(shí),會(huì)配置以Eureka為基礎(chǔ)的Ribbon。
-
類相關(guān)信息:
圖21 RibbonEurekaAutoConfiguration
① 裝配時(shí)機(jī)晚于 RibbonAutoConfiguration類
② 由于@RibbonClients注解的特性,會(huì)創(chuàng)建一個(gè)EurekaRibbonClientConfiguration的bean定義 到此,客戶端啟動(dòng)相關(guān)的核心配置類與負(fù)載均衡相關(guān)的核心配置類基本已經(jīng)分析完畢,單純的羅列源碼略顯枯燥無味,而且也不會(huì)加深記憶。
1.
Question1:為什么圖18中只會(huì)注入帶有@LoadBalanced注解的RestTemplate實(shí)例,而普通的RestTemplate不會(huì)注入進(jìn)去?
2.Question2:為什么RestTemplate帶有@LoadBalanced注解之后就具有負(fù)載均衡能力了?作者會(huì)在下一篇講解RestTemplate從配置到實(shí)現(xiàn)負(fù)載均衡的過程。
答疑連接:http://www.itdecent.cn/p/cb8344039efc
- ? 文章要是勘誤或者知識(shí)點(diǎn)說的不正確,歡迎評(píng)論,畢竟這也是作者通過閱讀源碼獲得的知識(shí),難免會(huì)有疏忽!
- ? 要是感覺文章對(duì)你有所幫助,不妨點(diǎn)個(gè)關(guān)注,或者移駕看一下作者的其他文集,也都是干活多多哦,文章也在全力更新中。
- ? 著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處!


















