1、Eureka client从注册中心更新服务列表,然后自身会做缓存;
2、作为服务消费者,就是从这些缓存信息中获取的服务提供者的信息;
3、增量更新的服务以30秒为周期循环调用;
4、增量更新数据在服务端保存时间为3分钟,因此Eureka client取得的数据虽然被称为"增量更新",仍然可能和30秒前取的数据一样,所以Eureka client要自己来处理重复信息;
5、由3、4两点可以推断出,Eureka client的增量更新,其实获取的是Eureka server最近三分钟内的变更,因此,如果Eureka client有超过三分钟没有做增量更新的话(例如网络问题),那么再调用增量更新接口时,那三分钟内Eureka server的变更就可能获取不到了,这就造成了Eureka server和Eureka client之间的数据不一致,需要有个方案来及时发现这个问题;
6、正常情况下,Eureka client多次增量更新后,最终的服务列表数据应该Eureka server保持一致,但如果期间发生异常,可能导致和Eureka server的数据不一致,为了暴露这个问题,Eureka server每次返回的增量更新数据中,会带有一致性哈希码,Eureka client用本地服务列表数据算出的一致性哈希码应该和Eureka server返回的一致,若不一致就证明增量更新出了问题导致Eureka client和Eureka server上的服务列表信息不一致了,此时需要全量更新;
7、Eureka server上的服务列表信息对外提供JSON/XML两种格式下载;
8、Eureka client使用jersey的SDK,去下载JSON格式的服务列表信息;
服务列表获取更新:
注释消息: 这可以确保对要获取的远程区域进行动态更改。
注释消息: 这两块区域都需要进行同步
注释消息: 只刷新映射以反映任何DNS/属性更改
更新服务列表,根据入参判断进行全量更新还是增量更新
private boolean fetchRegistry(boolean forceFullRegistryFetch) {
//用Stopwatch做耗时分析
Stopwatch tracer = FETCH_REGISTRY_TIMER.start();
try {
// 取出本地缓存的,之气获取的服务列表信息
Applications applications = getApplications();
//判断多个条件,确定是否触发全量更新,如下任一个满足都会全量更新:
//1. 是否禁用增量更新;
//2. 是否对某个region特别关注;
//3. 外部调用时是否通过入参指定全量更新;
//4. 本地还未缓存有效的服务列表信息;
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
{
//这些详细的日志可以看出触发全量更新的原因
logger.info("Disable delta property : {}", clientConfig.shouldDisableDelta());
logger.info("Single vip registry refresh property : {}", clientConfig.getRegistryRefreshSingleVipAddress());
logger.info("Force full registry fetch : {}", forceFullRegistryFetch);
logger.info("Application is null : {}", (applications == null));
logger.info("Registered Applications size is zero : {}",
(applications.getRegisteredApplications().size() == 0));
logger.info("Application version is -1: {}", (applications.getVersion() == -1));
//全量更新
getAndStoreFullRegistry();
} else {
//增量更新
getAndUpdateDelta(applications);
}
//重新计算和设置一致性hash码
applications.setAppsHashCode(applications.getReconcileHashCode());
//日志打印所有应用的所有实例数之和
logTotalInstances();
} catch (Throwable e) {
logger.error(PREFIX + appPathIdentifier + " - was unable to refresh its cache! status = " + e.getMessage(), e);
return false;
} finally {
if (tracer != null) {
tracer.stop();
}
}
//将本地缓存更新的事件广播给所有已注册的监听器,注意该方法已被CloudEurekaClient类重写
onCacheRefreshed();
//检查刚刚更新的缓存中,有来自Eureka server的服务列表,其中包含了当前应用的状态,
//当前实例的成员变量lastRemoteInstanceStatus,记录的是最后一次更新的当前应用状态,
//上述两种状态在updateInstanceRemoteStatus方法中作比较 ,如果不一致,就更新lastRemoteInstanceStatus,并且广播对应的事件
updateInstanceRemoteStatus();
return true;
}
全量更新:
private void getAndStoreFullRegistry() throws Throwable {
long currentUpdateGeneration = fetchRegistryGeneration.get();
logger.info("Getting all instance registry info from the eureka server");
Applications apps = null;
//由于并没有配置特别关注的region信息,因此会调用eurekaTransport.queryClient.getApplications方法从服务端获取服务列表
EurekaHttpResponse<Applications> httpResponse = clientConf