Eureka 自我保护机制

本文深入探讨了Eureka服务发现中的自我保护机制,详细解释了该机制如何防止在网络异常时误删健康微服务,包括关键变量、更新方法、EurekaServer初始化、EurekaClient注册与下线流程,以及自我保护触发条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是自我保护机制?

自我保护机制是一种应对网络异常的安全保护措施。它的架构哲学是宁可保留不健康的微服务,也不轻易注销健康的微服务。

Eureka Server 在运行期间会统计全部服务总体的心跳数,在15分钟内是否低于应该受到的85%。如果低于,则认为是网络异常问题,应该保护,Eureka Server会将当前的实例注册信息保护起来,同时提示一个警告。

一旦进入保护模式:

  1. Eureka Server将会不再删除服务注册表中的数据。也就是不会注销任何微服务。
  2. Eureka server仍然能够接受新服务的注册和查询请求,但是不会被同步到其他节点上,保证当前节点依然可用。

重要变量

类 AbstractInstanceRegistry

//每分钟最小续约阈值
//包含所有客户端,不分服务提供方和消费方
protected volatile int numberOfRenewsPerMinThreshold;

更新方法

protected void updateRenewsPerMinThreshold() {
    this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfClientsSendingRenews
            * (60.0 / serverConfig.getExpectedClientRenewalIntervalSeconds())
            * serverConfig.getRenewalPercentThreshold());
}

每分钟最小续约阈值 = 预期中每分钟发送续约消息的客户端数量 * (60s / 每分钟客户端预期续约间隔) * 续约百分比阈值

理解一下这个公式:

  • 每分钟最小续约阈值: Eureka Server 每分钟至少要收到这个数量的续约消息(心跳)。
  • 预期中每分钟发送续约消息的客户端数量 :注册到 Eureka Server 的客户端的数量
  • 60s / 每分钟客户端预期续约间隔:一分钟内,一个客户端的续约次数。每分钟客户端预期续约间隔,默认是 30s。所以一分钟内,一个客户端的续约次数默认是2次
  • 续约百分比阈值:默认 85%。

更新位置

EurekaServer 初始化

类 EurekaBootstrap

protected void initEurekaServerContext() throws Exception {
	EurekaServerConfig eurekaServerConfig = new DefaultEurekaServerConfig();
	//...
	registry.openForTraffic(applicationInfoManager, registryCount);
	//...
}

跟 initEurekaServerContext:
类 PeerAwareInstanceRegistryImpl

@Override
public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) {
    // Renewals happen every 30 seconds and for a minute it should be a factor of 2.
    //初始化expectedNumberOfClientsSendingRenews 
    this.expectedNumberOfClientsSendingRenews = count;
    //更新numberOfRenewsPerMinThreshold
    updateRenewsPerMinThreshold();
    //...
}

EurekaClient 主动下线

类 PeerAwareInstanceRegistryImpl

@Override
public boolean cancel(final String appName, final String id,
                      final boolean isReplication) {
    if (super.cancel(appName, id, isReplication)) {
        replicateToPeers(Action.Cancel, appName, id, null, null, isReplication);

        return true;
    }
    return false;
}

跟 cancel:
类 AbstractInstanceRegistry

@Override
public boolean cancel(String appName, String id, boolean isReplication) {
    return internalCancel(appName, id, isReplication);
}

跟 internalCancel:

protected boolean internalCancel(String appName, String id, boolean isReplication) {
	//...
	synchronized (lock) {
	    if (this.expectedNumberOfClientsSendingRenews > 0) {
	        //客户端数量减1
	        this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews - 1;
	        //更新numberOfRenewsPerMinThreshold
	        updateRenewsPerMinThreshold();
	    }
	}
	//...
}

EurekaClient 注册

类 PeerAwareInstanceRegistryImpl

@Override
public void register(final InstanceInfo info, final boolean isReplication) {
    int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS;
    if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) {
        leaseDuration = info.getLeaseInfo().getDurationInSecs();
    }
    super.register(info, leaseDuration, isReplication);
    replicateToPeers(Action.Register, info.getAppName(), info.getId(), info, null, isReplication);
}

跟 register:
类 AbstractInstanceRegistry

public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
	//...
	synchronized (lock) {
	    if (this.expectedNumberOfClientsSendingRenews > 0) {
	        //客户端数量加1
	        this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews + 1;
	        //更新numberOfRenewsPerMinThreshold
	        updateRenewsPerMinThreshold();
	    }
	}
	//...                
}

15分钟一次的定时任务

类 PeerAwareInstanceRegistryImpl

private void scheduleRenewalThresholdUpdateTask() {
    timer.schedule(new TimerTask() {
                       @Override
                       public void run() {
                           updateRenewalThreshold();
                       }
                   }, serverConfig.getRenewalThresholdUpdateIntervalMs(),
            serverConfig.getRenewalThresholdUpdateIntervalMs());
}
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;
                }
            }
        }
        synchronized (lock) {
            // Update threshold only if the threshold is greater than the
            // current expected threshold or if self preservation is disabled.
            if ((count) > (serverConfig.getRenewalPercentThreshold() * expectedNumberOfClientsSendingRenews)
                    || (!this.isSelfPreservationModeEnabled())) {
                this.expectedNumberOfClientsSendingRenews = count;
                //更新numberOfRenewsPerMinThreshold
                updateRenewsPerMinThreshold();
            }
        }
        logger.info("Current renewal threshold is : {}", numberOfRenewsPerMinThreshold);
    } catch (Throwable e) {
        logger.error("Cannot update renewal threshold", e);
    }
}

自我保护触发任务

类 AbstractInstanceRegistry

protected void postInit() {
    renewsLastMin.start();
    if (evictionTaskRef.get() != null) {
        evictionTaskRef.get().cancel();
    }
    evictionTaskRef.set(new EvictionTask());
    evictionTimer.schedule(evictionTaskRef.get(),
            serverConfig.getEvictionIntervalTimerInMs(),
            serverConfig.getEvictionIntervalTimerInMs());
}

跟 EvictionTask:
类 EvictionTask in AbstractInstanceRegistry
看 run 方法

class EvictionTask extends TimerTask {

    private final AtomicLong lastExecutionNanosRef = new AtomicLong(0l);

    @Override
    public void run() {
        try {
            long compensationTimeMs = getCompensationTimeMs();
            logger.info("Running the evict task with compensationTime {}ms", compensationTimeMs);
            evict(compensationTimeMs);
        } catch (Throwable e) {
            logger.error("Could not run the evict task", e);
        }
    }
    //...
}
public void evict(long additionalLeaseMs) {
    logger.debug("Running the evict task");
	//如果最后一分钟的续约数量没有达到每分钟最小续约阈值
    if (!isLeaseExpirationEnabled()) {
        logger.debug("DS: lease expiration is currently disabled.");
        //可以保护
        return;
    }
    //...
}

跟 isLeaseExpirationEnabled:
类 PeerAwareInstanceRegistryImpl

@Override
public boolean isLeaseExpirationEnabled() {
	//如果不需要自我保护机制
    if (!isSelfPreservationModeEnabled()) {
        // The self preservation mode is disabled, hence allowing the instances to expire.
        return true;
    }
    //最后一分钟的续约数量是否达到每分钟最小续约阈值
    return numberOfRenewsPerMinThreshold > 0 && getNumOfRenewsInLastMin() > numberOfRenewsPerMinThreshold;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值