Euerka源码-服务端处理获取注册表请求

154 阅读3分钟

获取注册表

请求入口

首先找到ApplicationsResource

//全量下载
public Response getContainers(...) {
        boolean isRemoteRegionRequested = null != regionsStr && !regionsStr.isEmpty();
        String[] regions = null;
        //是否请求远程region
        if (!isRemoteRegionRequested) {
            EurekaMonitors.GET_ALL.increment();
        } else {
            regions = regionsStr.toLowerCase().split(",");
            Arrays.sort(regions); // So we don't have different caches for same regions queried in different order.
            EurekaMonitors.GET_ALL_WITH_REMOTE_REGIONS.increment();
        }
        //注册表是否允许访问
        if (!registry.shouldAllowAccess(isRemoteRegionRequested)) {
            return Response.status(Status.FORBIDDEN).build();
        }
        //定义cacheKey
        Key cacheKey = new Key(Key.EntityType.Application,
                ResponseCacheImpl.ALL_APPS,
                keyType, CurrentRequestVersion.get(), EurekaAccept.fromString(eurekaAccept), regions
        );
​
       //下面简单的代码,核心代码为 responseCache.getGZIP(cacheKey) / responseCache.get(cacheKey)
}
//增量下载方式差不多这里不在赘述,方法入口
 public Response getContainerDifferential(...)

构建ResponseCacheImpl

ResponseCacheImpl 在读取的时候牵涉到了 只读缓存map 和 读写缓存map

在看获取数据之前先来看看ResponseCacheImpl的构造器

//只读缓存map
private final ConcurrentMap<Key, Value> readOnlyCacheMap = new ConcurrentHashMap<Key, Value>();
//读写缓存map
private final LoadingCache<Key, Value> readWriteCacheMap;
​
ResponseCacheImpl(EurekaServerConfig serverConfig, ServerCodecs serverCodecs, AbstractInstanceRegistry registry) {
  this.readWriteCacheMap =
    //初始化容量,默认只为1000
    CacheBuilder.newBuilder().initialCapacity(serverConfig.getInitialCapacityOfResponseCache())
    //过期时间 180s
    .expireAfterWrite(serverConfig.getResponseCacheAutoExpirationInSeconds(), TimeUnit.SECONDS)
    ...
    .build(new CacheLoader<Key, Value>() {
      @Override
      public Value load(Key key) throws Exception {
        ...
        //获取到value
        Value value = generatePayload(key);
        return value;
      }
    });
  //是否使用只读缓存,这个可以在配置EurekaServer的时候进行配置的
  if (shouldUseReadOnlyResponseCache) {
    //创建定时任务, 定时哥更新readOnlyCacheMap,根据时间responseCacheUpdateIntervalMs 默认是30s
    timer.schedule(getCacheUpdateTask(),
                   new Date(((System.currentTimeMillis() / responseCacheUpdateIntervalMs) * responseCacheUpdateIntervalMs)
                            + responseCacheUpdateIntervalMs),
                   responseCacheUpdateIntervalMs);
  }
}
//对应的task信息 getCacheUpdateTask()
//遍历 readOnlyCacheMap
for (Key key : readOnlyCacheMap.keySet()) {
  try {
    Value cacheValue = readWriteCacheMap.get(key);
    Value currentCacheValue = readOnlyCacheMap.get(key);
    //如果readOnlyCacheMap中的只和 readWriteCacheMap中的值不匹配的话,则进行赋值
    if (cacheValue != currentCacheValue) {
      readOnlyCacheMap.put(key, cacheValue);
    }
  } 
}

generatePayload方法详解

//这里只看全量的处理
if (ALL_APPS.equals(key.getName())) {
  boolean isRemoteRegionRequested = key.hasRegions();
  if (isRemoteRegionRequested) {
    //获取注册表信息
     payload = getPayLoad(key, registry.getApplicationsFromMultipleRegions(key.getRegions()));
  } else {
    //获取注册表信息
     payload = getPayLoad(key, registry.getApplications());
   }
}else {
  if (isRemoteRegionRequested) {
    payload = getPayLoad(key, registry.getApplicationDeltasFromMultipleRegions(key.getRegions()));
  } else {
    payload = getPayLoad(key, registry.getApplicationDeltas());
  }
}
​

获取注册表

AbstractInstanceRegistry获取本地注册表和远端注册表 以及 增量获取

public Applications getApplicationsFromMultipleRegions(String[] remoteRegions) {
  //是否包含远程region
  boolean includeRemoteRegion = null != remoteRegions && remoteRegions.length != 0;
  Applications apps = new Applications();
  apps.setVersion(1L);
  //获取本地注册表信息,然后遍历,给apps进行赋值操作
  ...
  if (includeRemoteRegion) {
    for (String remoteRegion : remoteRegions) {
      RemoteRegionRegistry remoteRegistry = regionNameVSRemoteRegistry.get(remoteRegion);
      if (null != remoteRegistry) {
        //获取远端的Applications
        Applications remoteApps = remoteRegistry.getApplications();
        for (Application application : remoteApps.getRegisteredApplications()) {
          if (shouldFetchFromRemoteRegistry(application.getName(), remoteRegion)) {
            //根据服务名获取本地的application
            Application appInstanceTillNow = apps.getRegisteredApplications(application.getName());
            //如果本地为不为空,则使用本地的,如果本地为空则使用远端的
            if (appInstanceTillNow == null) {
              appInstanceTillNow = new Application(application.getName());
              apps.addApplication(appInstanceTillNow);
            }
            for (InstanceInfo instanceInfo : application.getInstances()) {
              appInstanceTillNow.addInstance(instanceInfo);
            }
          ....
}
          
//增量获取
public Applications getApplicationDeltasFromMultipleRegions(String[] remoteRegions) {
   Applications apps = new Applications();
   apps.setVersion(responseCache.getVersionDeltaWithRegions().get());
   Map<String, Application> applicationInstancesMap = new HashMap<String, Application>();
   try {
     write.lock();
     //从recentlyChangedQueue中进行获取,recentlyChangedQueue表示的是最近变更的,前面源码分析的时候有相关的说明
     Iterator<RecentlyChangedItem> iter = this.recentlyChangedQueue.iterator();
     while (iter.hasNext()) {
       ...
       //这里和全量差不多,唯一的差别就是 new InstanceInfo了, 这样做的目的是防止recentlyChangedQueue中相关值变更了,引起返回的值变化
       app.addInstance(new InstanceInfo(decorateInstanceInfo(lease)));
     }
   } finally {
     write.unlock();
   }
 }
//recentlyChangedQueue中的值 定期会进行清除的    
protected AbstractInstanceRegistry(EurekaServerConfig serverConfig, EurekaClientConfig clientConfig, ServerCodecs serverCodecs) {
  //默认是延迟30s,每隔30s执行一次操作      
  this.deltaRetentionTimer.schedule(getDeltaRetentionTask(),
                serverConfig.getDeltaRetentionTimerIntervalInMs(),
                serverConfig.getDeltaRetentionTimerIntervalInMs());
}
//getDeltaRetentionTask, 其他getRetentionTimeInMSInDeltaQueue 这个是可以配置的,默认是180s
Iterator<RecentlyChangedItem> it = recentlyChangedQueue.iterator();
          while (it.hasNext()) {
            if (it.next().getLastUpdateTime() <
                System.currentTimeMillis() - serverConfig.getRetentionTimeInMSInDeltaQueue()) {
              it.remove();
            } else {
              break;
            }
          }
          

介绍完缓存中的数据来源之后,现在就开始看从缓存获取数据的处理吧

从缓存中获取数据

从缓存中获取数据

public byte[] getGZIP(Key key) {
  //是否从只读响应缓存中获取 默认为true
  Value payload = getValue(key, shouldUseReadOnlyResponseCache);
  if (payload == null) {
    return null;
  }
  return payload.getGzipped();
}
​
Value getValue(final Key key, boolean useReadOnlyCache) {
  Value payload = null;
  try {
    //这个值是上面传过来的 默认为true
    if (useReadOnlyCache) {
      //从只读缓存map中获取数据
      final Value currentPayload = readOnlyCacheMap.get(key);
      if (currentPayload != null) {
        payload = currentPayload;
      } else {
        //则从读写map中获取数据
        payload = readWriteCacheMap.get(key);
        //readOnlyCacheMap 定时更新的时候,一开始里面是空的,则会从readwirtemap中获取,后续readOnlyCacheMap中的值才会自动更新
        readOnlyCacheMap.put(key, payload);
      }
    } else {
      payload = readWriteCacheMap.get(key);
    }
  } catch (Throwable t) {
    logger.error("Cannot get value for key : {}", key, t);
  }
  return payload;
}

总结

  1. 获取注册表包括了,增量获取 和 全量获取
  2. 需要注意和思考的是,全量获取的时候 读写分离的设计