客户端注册表更新
下载注册表有两种情况:全量下载和增量下载
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数量不一致)则进行全量下载