initializedResponseCache():
精辟,缓存来实现返回结果的缓存,优秀设计啊。
使用goole cache初始化一个缓存类ResponseCacheImpl
,缓存(all applications, delta changes and for individual applications
)请求的结果, 此类中有两个缓存:
readWriteCacheMap
: 读写缓存。初始化容量1000,失效时间3分钟。readOnlyCacheMap
:只读缓存,shouldUseReadOnlyResponseCache
属性控制是否启用,默认是启用的。此缓存会使用,名为Eureka-CacheFillTimer
的timer,每30s更新从readWriteCacheMap
中更新readOnlyCacheMap
中的缓存值。
取值逻辑: 先从readOnlyCacheMap
取值,没有去readWriteCacheMap
,没有去通过CacheLoader加载,而CacheLoader会到维护应用实例注册信息的Map中获取。
这里就产生了一个疑问,为啥有搞个二级缓存来缓存结果呢?不是很理解。
scheduleRenewalThresholdUpdateTask()
使用名为ReplicaAwareInstanceRegistry - RenewalThresholdUpdater
的timer,每15(900s)分钟执行updateRenewalThreshold()
任务,更新续约阀值。
private void updateRenewalThreshold() { try { Applications apps = eurekaClient.getApplications(); int count = 0; //统计所有注册instance个数 for (Application app : apps.getRegisteredApplications()) { for (InstanceInfo instance : app.getInstances()) { if (this.isRegisterable(instance)) { ++count; } } } synchronized (lock) { 当总数》预期值时 或者 关闭了自我保护模式,更新 if ((count * 2) > (serverConfig.getRenewalPercentThreshold() * expectedNumberOfRenewsPerMin) || (!this.isSelfPreservationModeEnabled())) { this.expectedNumberOfRenewsPerMin = count * 2; this.numberOfRenewsPerMinThreshold = (int) ((count * 2) * serverConfig.getRenewalPercentThreshold()); } } logger.info("Current renewal threshold is : {}", numberOfRenewsPerMinThreshold); } catch (Throwable e) { logger.error("Cannot update renewal threshold", e); } }
expectedNumberOfRenewsPerMin
每分钟最大的续约数量 (30s/次,2次/s): =客户端数量count*2numberOfRenewsPerMinThreshold
每分钟续约阈值。serverConfig.getRenewalPercentThreshold()*expectedNumberOfRenewsPerMin
serverConfig.getRenewalPercentThreshold()默认是0.85
当每分钟续约数小于numberOfRenewsPerMinThreshold
阈值时,并且自我保护没有关闭的情况下,开启自我保护,此期间不剔除任何一个客户端。(下面的EvictionTask()
驱逐任务会讲到如何利用)
- InstanceRegistry初始化
- 客户端cancle主动下线
- 客户端注册
- scheduleRenewalThresholdUpdateTask
此四个地方都会更新两个值
initRemoteRegionRegistry()
初始化 远程区域注册 相关信息
2.3 @PreDestroy注解的initialize方法
@PreDestroy
修饰的方法会在服务器卸载Servlet的时候执行,并且只会被服务器执行一次,被@PreDestroy
修饰的方法会Destroy
方法之后执行,在Servlet被彻底卸载之前.
public void shutdown() { registry.shutdown(); peerEurekaNodes.shutdown(); }
registry.shutdown();
停掉init()时启动的定时任务
peerEurekaNodes.shutdown()
清空集群url缓存,集群节点缓存。
2.4 小结
总结:EurekaServerContext
的初始化做了很多事情,很精辟,建议多阅读,多学习
3.EurekaServer启动:
EurekaServerInitializerConfiguration
实现了SmartLifecycle
接口,在spring启动后,执行start()方法
eurekaServerBootstrap.contextInitialized(EurekaServerInitializerConfiguration.this.servletContext); log.info("Started Eureka Server"); //发布注册中心可以注册事件 publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig())); //状态为运行状态 EurekaServerInitializerConfiguration.this.running = true; //发布注册中心启动完成事件 publish(new EurekaServerStartedEvent(getEurekaServerConfig()));
这里重点看先EurekaServerBootstrap.contextInitialized
EurekaServerBootstrap
的contextInitialized主要干了两件事
initEurekaEnvironment();初始化环境 initEurekaServerContext();初始化上下文
3.1 initEurekaEnvironment
主要是数据中心等环境变量的初始化
3.2 initEurekaServerContext
此方法中最重要的是
从相邻eureka节点拷贝注册列表信息 int registryCount = this.registry.syncUp(); 允许开始与客户端的数据传输,即开始作为Server服务 this.registry.openForTraffic(this.applicationInfoManager, registryCount);
3.1.1 registry.syncUp()
@Override public int syncUp() { // Copy entire entry from neighboring DS node int count = 0; //重试次数 for (int i = 0; ((i < serverConfig.getRegistrySyncRetries()) && (count == 0)); i++) { if (i > 0) { try { Thread.sleep(serverConfig.getRegistrySyncRetryWaitMs()); } catch (InterruptedException e) { logger.warn("Interrupted during registry transfer.."); break; } } //从eurekaClient获取服务列表 Applications apps = eurekaClient.getApplications(); //遍历注册 for (Application app : apps.getRegisteredApplications()) { for (InstanceInfo instance : app.getInstances()) { try { if (isRegisterable(instance)) { register(instance, instance.getLeaseInfo().getDurationInSecs(), true); count++; } } catch (Throwable t) { logger.error("During DS init copy", t); } } } } return count; }
3.1.2 registry.openForTraffic
允许开始与客户端的数据传输,即开始作为Server服务
InstanceRegistry.openForTraffic public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) { super.openForTraffic(applicationInfoManager, count == 0 ? this.defaultOpenForTrafficCount : count); } PeerAwareInstanceRegistryImpl.openForTraffic public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) { //每分钟期待的续约数(默认30s续约,60s就是2次) this.expectedNumberOfRenewsPerMin = count * 2; // 每分钟续约的阀值:0.85 * expectedNumberOfRenewsPerMin this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold()); .... logger.info("Changing status to UP"); //applicationInfoManager设置状态为UP applicationInfoManager.setInstanceStatus(InstanceStatus.UP); super.postInit(); }
3.1.3 EvictionTask() 驱逐任务
protected void postInit() { //又启动了一个续约数统计器,此统计器用于配合驱逐任务 renewsLastMin.start(); if (evictionTaskRef.get() != null) { evictionTaskRef.get().cancel(); } evictionTaskRef.set(new EvictionTask()); evictionTimer.schedule(evictionTaskRef.get(), serverConfig.getEvictionIntervalTimerInMs(), serverConfig.getEvictionIntervalTimerInMs()); }
创建一个名为Eureka-EvictionTimer
的定时器来执行EvictionTask()任务。 EvictionTask()任务:
@Override public void run() { // 获取延迟秒数,就是延迟几秒下线时间。 long compensationTimeMs = getCompensationTimeMs(); //驱逐操作 evict(compensationTimeMs); }
evict()驱逐操作:清理过期租约