Netflix Ribbon - Ribbon整合Eureka - 获取及更新服务注册列表

284 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。


Ribbon整合Eureka - 获取服务注册列表

拦截器处理RestTemplate,将请求交由RibbonLoadBalancerClient处理。RibbonLoadBalancerClient内部使用默认的ZoneAwareLoadBalancer,通过装饰器模式使用与Eureka整合的具备获取服务实例列表能力的DiscoveryEnabledNIWSServerList组件获取服务实例列表。


  1. 通过服务名 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中去。