本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Ribbon整合Eureka - 获取服务注册列表
拦截器处理RestTemplate,将请求交由RibbonLoadBalancerClient处理。RibbonLoadBalancerClient内部使用默认的ZoneAwareLoadBalancer,通过装饰器模式使用与Eureka整合的具备获取服务实例列表能力的DiscoveryEnabledNIWSServerList组件获取服务实例列表。
- 通过服务名 serviceId 获取与其他服务配置隔离的ILoadBalancer。
LoadBalancerInterceptor
@LoadBalanced
META-INF/spring.factories
LoadBalancerAutoConfiguration
LoadBalancerInterceptor(LoadBalancerClient & LoadBalancerRequestFactory)
RestTemplateConsumer
LoadBalancerClient & RibbonLoadBalancerClient
RibbonAutoConfiguration
SpringClientFactory
RibbonLoadBalancerClient
SpringClientFactory
RibbonClientConfiguration
ILoadBalaner
RibbonClientConfiguration
ServerList<Server>
ILoadBalancer & ZoneAwareLoadBalancer implement DynamicServerListLoadBalancer implement BaseLoadBalancer
ServerList<Server>
EurekaRibbonClientConfiguration
DomainExtractingServerList(DiscoveryEnabledNIWSServerList) implement ServerList
LoadBalancerRequestFactory
占位符之 --- 后续更新
琥珀色黄昏像糖在很美的远方
你的脸没有化妆我却疯狂爱上
占位符之 --- 后续更新
LoadBalancer内部,必须要去获取到当前要访问的这个服务的server list。在LoadBalancer实例内部,必须是有这个服务的server list。
// DynamicServerListLoadBalancer.restOfInit
void restOfInit(IClientConfig clientConfig) {
boolean primeConnection = this.isEnablePrimingConnections();
// turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
this.setEnablePrimingConnections(false);
enableAndInitLearnNewServersFeature();
updateListOfServers();
if (primeConnection && this.getPrimeConnections() != null) {
this.getPrimeConnections()
.primeConnections(getReachableServers());
}
this.setEnablePrimingConnections(primeConnection);
LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
}
enableAndInitLearnNewServersFeature():如果后续ServiceA有新的服务实例加入进来了,能够感知到新加入进来的服务实例
updateListOfServers():更新server list。在创建ZoneAwareLoadBalancer实例的时候,通过调用其父类DynamicServerListLoadBalancer的构造函数,调用restOfInit()方法,调用updateListOfServers()方法,从eureka client那里获取到ServiceA的server list
// EurekaRibbonClientConfiguration.ribbonServerList
@Bean
@ConditionalOnMissingBean
public ServerList<?> ribbonServerList(IClientConfig config,
Provider<EurekaClient> eurekaClientProvider) {
if (this.propertiesFactory.isSet(ServerList.class, serviceId)) {
return this.propertiesFactory.get(ServerList.class, config, serviceId);
}
// 真正与Eureka整合,获取服务实例的ServerList实现类
DiscoveryEnabledNIWSServerList discoveryServerList = new DiscoveryEnabledNIWSServerList(
config, eurekaClientProvider);
DomainExtractingServerList serverList = new DomainExtractingServerList(
discoveryServerList, config, this.approximateZoneFromHostname);
return serverList;
}
实际上跟eureka整合的ServerList提供的getUpdatedServerList()方法,是调用的DiscoveryEnabledNIWSServerList的getUpdatedServerList()方法:
// DiscoveryEnabledNIWSServerList.getUpdatedListOfServers
@Override
public List<DiscoveryEnabledServer> getUpdatedListOfServers(){
return obtainServersViaDiscovery();
}
// DiscoveryEnabledNIWSServerList.obtainServersViaDiscovery
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 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
// 通过vipAddress(serviceId)获取服务实例列表
List<InstanceInfo> listOfInstanceInfo = eurekaClient.getInstancesByVipAddress(vipAddress, isSecure, targetRegion);
for (InstanceInfo ii : listOfInstanceInfo) {
// 留取 UP 状态的服务实例
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;
}
在obtainServersViaDiscovery()方法里面,从eureka client中获取到注册表,从注册表里可以获取到当前这个服务的ServiceA对应的server list,vipAddress(服务名,ServiceA)。
Ribbon整合Eureka - 注册表持续更新
LoadBalancerInterceptor ->
LoadBalancerClient ->
ILoadBalancer & ZoneAwareLoadBalancer & DynamicServerListLoadBalancer ->
ServerList & DomainExtractingServerList & DiscoveryEnabledNIWSServerList ->
ServerListUpdater & PollingServerListUpdater
PollingServerListUpdater 定时更新ServerList
eureka client不断的去从eureka server每隔30秒更新一次注册表,拉取增量注册表。ribbon和eureka整合的机制里,ServerListUpdater组件,负责每隔30秒,从本地的eureka client里刷新一下服务的注册表到LoadBalancer中。
enableAndInitLearnNewServersFeature();
ZoneAwareLoadBalancer初始化构造的时候,传入ServerListUpdater的实现类PollingServerListUpdater。
在PollingServerListUpdater的start()方法中传入UpdateAction(updateListOfServers()方法)组件,代表的是实际的更新注册表的行为。
在PollingServerListUpdater中,创建了一个Runnable线程执行UpdateAction的行为。在延迟1秒过后,每隔30秒启动Runnable线程,执行UpdateAction中的操作刷新注册表。从eureka client中获取注册表,然后刷新到LoadBalancer中去。