获取注册表
请求入口
首先找到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;
}
总结
- 获取注册表包括了,增量获取 和 全量获取
- 需要注意和思考的是,全量获取的时候 读写分离的设计