Eureka源码-客户端注册表更新

589 阅读3分钟

客户端注册表更新

下载注册表有两种情况:全量下载和增量下载

private boolean fetchRegistry(boolean forceFullRegistryFetch) {
  try {
    //第一次获取注册表,applications为空或者已经注册到Eureka中的应用size为0
    //禁用增量更新、强制全量获取、本地注册表数
    //或者配置了vipAddress
    Applications applications = getApplications();
    if (clientConfig.shouldDisableDelta()
        || (!Strings.isNullOrEmpty(clientConfig.getRegistryRefreshSingleVipAddress()))
        || forceFullRegistryFetch
        || (applications == null)
        || (applications.getRegisteredApplications().size() == 0)
        || (applications.getVersion() == -1)) //Client application does not have latest library supporting delta
    {
      //全量下载
      getAndStoreFullRegistry();
    } else {
      //增量更新
      getAndUpdateDelta(applications);
    }
  }
  // Notify about cache refresh before updating the instance remote status
  onCacheRefreshed();
  // Update remote status based on refreshed data held in the cache
  updateInstanceRemoteStatus();
  // registry was fetched successfully, so return true
  return true;
}

全量下载

全量下载相对来说比较简单

private void getAndStoreFullRegistry() throws Throwable {
  Applications apps = null;
  EurekaHttpResponse<Applications> httpResponse = 
    clientConfig.getRegistryRefreshSingleVipAddress() == null
    ? eurekaTransport.queryClient.getApplications(remoteRegionsRef.get())
    : eurekaTransport.queryClient.getVip(clientConfig.getRegistryRefreshSingleVipAddress(), remoteRegionsRef.get());
  if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) {
    //从EurekaServer获取所有的注册信息
    apps = httpResponse.getEntity();
  }
  if (fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, currentUpdateGeneration + 1)) {
    //添加到本地缓存中(把返回的数据顺序进行打算处理,以及过滤掉状态是非在线的服务保留在线服务
    //(通过config中的shouldFilterOnlyUpInstances进行配置为 true))
    localRegionApps.set(this.filterAndShuffle(apps));
  }
}

增量下载

private void getAndUpdateDelta(Applications applications) throws Throwable {
  Applications delta = null;
  //获取增量注册表信息
  EurekaHttpResponse<Applications> httpResponse = eurekaTransport.queryClient.
  getDelta(remoteRegionsRef.get());
  if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) {
    delta = httpResponse.getEntity();
  }
  //如果增量请求数据为空,则进行全量下载
  if (delta == null) {
    getAndStoreFullRegistry();
  } else if (fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, currentUpdateGeneration + 1)) {
    String reconcileHashCode = "";
    if (fetchRegistryUpdateLock.tryLock()) {
      try {
        //更新本地注册表
        updateDelta(delta);
        //获取注册表的hashcode信息,感兴趣的可以在跟进去看看具体的实现逻辑
        reconcileHashCode = getReconcileHashCode(applications);
      } finally {
        fetchRegistryUpdateLock.unlock();
      }
 } 
    // 如果delta获取的的instance数据量和本地注册表的数量不一致 则进行全量下载
    if (!reconcileHashCode.equals(delta.getAppsHashCode()) || clientConfig.shouldLogDeltaDiff()) {
      reconcileAndLogDifference(delta, reconcileHashCode);  // this makes a remoteCall
    }
  }
}
private void updateDelta(Applications delta) {
  int deltaCount = 0;
  for (Application app : delta.getRegisteredApplications()) {
    for (InstanceInfo instance : app.getInstances()) {
      //本地注册表信息
      Applications applications = getApplications();
      //获取实例的region
      String instanceRegion = instanceRegionChecker.getInstanceRegion(instance);
      //判断是否是本地region
      if (!instanceRegionChecker.isLocalRegion(instanceRegion)) {
        Applications remoteApps = remoteRegionVsApps.get(instanceRegion);
        if (null == remoteApps) {
          remoteApps = new Applications();
          remoteRegionVsApps.put(instanceRegion, remoteApps);
        }
        //如果是非localRegion则把远程Apps赋值给applications
        applications = remoteApps;
      }
      ++deltaCount;
      if (ActionType.ADDED.equals(instance.getActionType())) {
        //从本地注册表中获取Application
        Application existingApp = applications.getRegisteredApplications(instance.getAppName());
        //如果不存在,则把增量更新到的这个app添加到applications中
        if (existingApp == null) {
          //把application添加到注册表中
          applications.addApplication(app);
        }
        //然后把app的实例信息添加到实例列表里面
        //看下面addInstance的注释
        applications.getRegisteredApplications(instance.getAppName()).addInstance(instance);
      } else if (ActionType.MODIFIED.equals(instance.getActionType())) {
        Application existingApp = applications.getRegisteredApplications(instance.getAppName());
        if (existingApp == null) {
          applications.addApplication(app);
        }
        applications.getRegisteredApplications(instance.getAppName()).addInstance(instance);
      } else if (ActionType.DELETED.equals(instance.getActionType())) {
        Application existingApp = applications.getRegisteredApplications(instance.getAppName());
        if (existingApp != null) {
          //从本地注册表中移除实例
          existingApp.removeInstance(instance);
          //如果本地注册表中该应用下面没有实例信息了,则移除该应用
       if (existingApp.getInstancesAsIsFromEureka().isEmpty()) {
            applications.removeApplication(existingApp);
          }
        }
      }
    }
  }
}
public void addInstance(InstanceInfo i) {
  instancesMap.put(i.getId(), i);
  synchronized (instances) {
    //instances 是set类型的,并且instanceInfo重写equals方法,
    //在更新的时候 如果instanceId不变,不进行移除是不会覆盖的,
    //所以先进行remove然后在进行add
    instances.remove(i);
    instances.add(i);
    isDirty = true;
  }
}

总结

全量下载时机或者条件

  • 第一次获取注册表
  • 禁用增量下载
  • 配置了VIPaddress
  • 增量更新没有获取数据时,进行全量下载
  • 增量数据和本地注册表的数据的hashcode(或者说instance数量不一致)则进行全量下载