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


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

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;
}
  1. 全量下载

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

    private void getAndStoreFullRegistry() throws Throwable {
      Applications apps = null;
      EurekaHttpResponse 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));
      }
    }
    
  2. 增量下载

    private void getAndUpdateDelta(Applications applications) throws Throwable {
      Applications delta = null;
      //获取增量注册表信息
      EurekaHttpResponse 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();
          }
        } 
        // There is a diff in number of instances for some reason
        // 如果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;
      }
    }
    

总结:

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