Spring中Eureka的自我保护详解
作者:dalianpai
Eureka的自我保护
当Eureka Server节点在短时间内丢失过多客户端时(可能发生了网络分区故障),那么这个节点就会进入自我保护模式。一旦进入该模式,Eureka Server就会保护服务注册表中的信息,不再删除服务注册表中的数据(也就是不会注销任何微服务)。当网络故障恢复后,该Eureka Server节点会自动退出自我保护模式。
如果想研究自我保护机制,那就要从服务下线那里,因为自我保护来决定是让eureka进行剔除服务的,下线代码如下:
public void evict(long additionalLeaseMs) { logger.debug("Running the evict task"); //是否允许主动摘除故障的服务实例 if (!isLeaseExpirationEnabled()) { logger.debug("DS: lease expiration is currently disabled."); return; } .... }
@Override public boolean isLeaseExpirationEnabled() { //默认是true,关闭自我保护机制的话,这个方法永远返回true,随时都可以清除故障的服务实例。 if (!isSelfPreservationModeEnabled()) { // The self preservation mode is disabled, hence allowing the instances to expire. return true; } //numberOfRenewsPerMinThreshold 这个是我期望的一分钟发送多少心跳。 //getNumOfRenewsInLastMin 这个是上一分钟一共发来多少心跳 return numberOfRenewsPerMinThreshold > 0 && getNumOfRenewsInLastMin() > numberOfRenewsPerMinThreshold; }
触发的机制就是说,如果最近一分钟发过来的心跳小于期望的值,这进入心跳保护机制。那么就来看一下,这个期望的值是什么算出来的。
首先在初始化的时候,就会给他先赋值,默认值为其他server拉着的注册表信息的数量乘以2
// EurekaBootStrap.java protected void initEurekaServerContext() throws Exception { // ... 省略其它代码 // 【2.2.10】从其他 Eureka-Server 拉取注册信息 // Copy registry from neighboring eureka node int registryCount = registry.syncUp(); registry.openForTraffic(applicationInfoManager, registryCount); // ... 省略其它代码 } // PeerAwareInstanceRegistryImpl.java @Override public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) { // Renewals happen every 30 seconds and for a minute it should be a factor of 2. this.expectedNumberOfRenewsPerMin = count * 2; this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());
然后再上线,下线,故障的时候都会对他进行修改。
由于以前的版本为硬编码,是直接乘以2的,近期的版本,对代码进行了优化,因为心跳是30秒发来一次,所以直接用60/30,然后在乘以0.85。
为什么乘以续租百分比
低于这个百分比,意味着开启自我保护机制。默情况下,eureka.renewalPercentThreshold = 0.85 。
如果你真的调整了续租频率,可以等比去续租百分比,以保证合适的触发自我保护机制的阀值。另外,你需要注意,续租频率是 Client 级别,续租百分比是 Server 级别。
protected void updateRenewsPerMinThreshold() { this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfClientsSendingRenews * (60.0 / serverConfig.getExpectedClientRenewalIntervalSeconds()) * serverConfig.getRenewalPercentThreshold()); }
以前的代码是有bug的,只对下线的代码进行了修改,故障是没有的,近期版本也做了修复。把剔除服务的相关代码都抽取了出来了,形成了internalCancel方法,被下面的2个方法调用,在它的方法里,做了减法。
定时更新
Eureka-Server 定时重新计算 numberOfRenewsPerMinThreshold 、expectedNumberOfRenewsPerMin 。
实现代码如下:
// PeerAwareInstanceRegistryImpl.java private void scheduleRenewalThresholdUpdateTask() { timer.schedule(new TimerTask() { @Override public void run() { updateRenewalThreshold(); } }, serverConfig.getRenewalThresholdUpdateIntervalMs(), serverConfig.getRenewalThresholdUpdateIntervalMs()); } // AbstractInstanceRegistry.java /** * 自我保护机锁 * * 当计算如下参数时使用: * 1. {@link #numberOfRenewsPerMinThreshold} * 2. {@link #expectedNumberOfRenewsPerMin} */ protected final Object lock = new Object(); private void updateRenewalThreshold() { try { // 计算 应用实例数 Applications apps = eurekaClient.getApplications(); int count = 0; for (Application app : apps.getRegisteredApplications()) { for (InstanceInfo instance : app.getInstances()) { if (this.isRegisterable(instance)) { ++count; } } } // 计算 expectedNumberOfRenewsPerMin 、 numberOfRenewsPerMinThreshold 参数 synchronized (lock) { // Update threshold only if the threshold is greater than the // current expected threshold of if the self preservation is disabled. if ((count * 2) > (serverConfig.getRenewalPercentThreshold() * numberOfRenewsPerMinThreshold) || (!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); } }
Registry注册表,默认是15分钟,会跑一次定时任务,算一下服务实例的数量,如果从别的eureka server拉取到的服务实例的数量,大于当前的服务实例的数量,会重新计算一下,主要是跟其他的eureka server做一下同步
到此这篇关于Spring中Eureka的自我保护详解的文章就介绍到这了,更多相关Eureka的自我保护内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!