eureka心跳_理解eureka的自我保护机制

相关参数

eureka.instance.leaseRenewalIntervalInSeconds = 30 # hearbeat interval

eureka.server.renewalPercentThreshold = 0.85

eureka.server.renewalThresholdUpdateIntervalMs = 15 * 60 * 1000 # 15mins

leaseRenewalIntervalInSeconds

client发送心跳的频率

renewalPercentThreshold

触发自我保护的心跳数比例阈值

renewalThresholdUpdateIntervalMs

多久重置一下心跳阈值

计算公式

Number of heartbeats expected from one client instance / min

factor = 60/leaseRenewalIntervalInSeconds

Number of heartbeats expected from N instances / min

factor * N

Minimum expected heartbeat threshold / min

factor N renewalPercentThreshold

源代码

this.expectedNumberOfRenewsPerMin = N * 2;

this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());

eureka启动时

eureka-core-1.4.12-sources.jar!/com/netflix/eureka/registry/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());

logger.info("Got " + count + " instances from neighboring DS node");

logger.info("Renew threshold is: " + numberOfRenewsPerMinThreshold);

this.startupTime = System.currentTimeMillis();

if (count > 0) {

this.peerInstancesTransferEmptyOnStartup = false;

}

DataCenterInfo.Name selfName = applicationInfoManager.getInfo().getDataCenterInfo().getName();

boolean isAws = Name.Amazon == selfName;

if (isAws && serverConfig.shouldPrimeAwsReplicaConnections()) {

logger.info("Priming AWS connections for all replicas..");

primeAwsReplicas(applicationInfoManager);

}

logger.info("Changing status to UP");

applicationInfoManager.setInstanceStatus(InstanceStatus.UP);

super.postInit();

}

count为eureka个数,此时expectedNumberOfRenewsPerMin为2(1个注册中心,每30秒发送一次心跳,则每分钟对于每个client,应该发送2次心跳),numberOfRenewsPerMinThreshold=(int)2*0.85 =1

client注册时

eureka-core-1.4.12-sources.jar!/com/netflix/eureka/registry/AbstractInstanceRegistry.java

public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {

try {

read.lock();

Map> gMap = registry.get(registrant.getAppName());

REGISTER.increment(isReplication);

if (gMap == null) {

final ConcurrentHashMap> gNewMap = new ConcurrentHashMap>();

gMap = registry.putIfAbsent(registrant.getAppName(), gNewMap);

if (gMap == null) {

gMap = gNewMap;

}

}

Lease existingLease = gMap.get(registrant.getId());

// Retain the last dirty timestamp without overwriting it, if there is already a lease

if (existingLease != null && (existingLease.getHolder() != null)) {

Long existingLastDirtyTimestamp = existingLease.getHolder().getLastDirtyTimestamp();

Long registrationLastDirtyTimestamp = registrant.getLastDirtyTimestamp();

logger.debug("Existing lease found (existing={}, provided={}", existingLastDirtyTimestamp, registrationLastDirtyTimestamp);

if (existingLastDirtyTimestamp > registrationLastDirtyTimestamp) {

logger.warn("There is an existing lease and the existing lease's dirty timestamp {} is greater" +

" than the one that is being registered {}", existingLastDirtyTimestamp, registrationLastDirtyTimestamp);

logger.warn("Using the existing instanceInfo instead of the new instanceInfo as the registrant");

registrant = existingLease.getHolder();

}

} else {

// The lease does not exist and hence it is a new registration

synchronized (lock) {

if (this.expectedNumberOfRenewsPerMin > 0) {

// Since the client wants to cancel it, reduce the threshold

// (1

// for 30 seconds, 2 for a minute)

this.expectedNumberOfRenewsPerMin = this.expectedNumberOfRenewsPerMin + 2;

this.numberOfRenewsPerMinThreshold =

(int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());

}

}

logger.debug("No previous lease information found; it is new registration");

}

Lease lease = new Lease(registrant, leaseDuration);

if (existingLease != null) {

lease.setServiceUpTimestamp(existingLease.getServiceUpTimestamp());

}

gMap.put(registrant.getId(), lease);

synchronized (recentRegisteredQueue) {

recentRegisteredQueue.add(new Pair(

System.currentTimeMillis(),

registrant.getAppName() + "(" + registrant.getId() + ")"));

}

// This is where the initial state transfer of overridden status happens

if (!InstanceStatus.UNKNOWN.equals(registrant.getOverriddenStatus())) {

logger.debug("Found overridden status {} for instance {}. Checking to see if needs to be add to the "

+ "overrides", registrant.getOverriddenStatus(), registrant.getId());

if (!overriddenInstanceStatusMap.containsKey(registrant.getId())) {

logger.info("Not found overridden id {} and hence adding it", registrant.getId());

overriddenInstanceStatusMap.put(registrant.getId(), registrant.getOverriddenStatus());

}

}

InstanceStatus overriddenStatusFromMap = overriddenInstanceStatusMap.get(registrant.getId());

if (overriddenStatusFromMap != null) {

logger.info("Storing overridden status {} from map", overriddenStatusFromMap);

registrant.setOverriddenStatus(overriddenStatusFromMap);

}

// Set the status based on the overridden status rules

InstanceStatus overriddenInstanceStatus = getOverriddenInstanceStatus(registrant, existingLease, isReplication);

registrant.setStatusWithoutDirty(overriddenInstanceStatus);

// If the lease is registered with UP status, set lease service up timestamp

if (InstanceStatus.UP.equals(registrant.getStatus())) {

lease.serviceUp();

}

registrant.setActionType(ActionType.ADDED);

recentlyChangedQueue.add(new RecentlyChangedItem(lease));

registrant.setLastUpdatedTimestamp();

invalidateCache(registrant.getAppName(), registrant.getVIPAddress(), registrant.getSecureVipAddress());

logger.info("Registered instance {}/{} with status {} (replication={})",

registrant.getAppName(), registrant.getId(), registrant.getStatus(), isReplication);

} finally {

read.unlock();

}

}

其中,重点看这段

synchronized (lock) {

if (this.expectedNumberOfRenewsPerMin > 0) {

// Since the client wants to cancel it, reduce the threshold

// (1

// for 30 seconds, 2 for a minute)

this.expectedNumberOfRenewsPerMin = this.expectedNumberOfRenewsPerMin + 2;

this.numberOfRenewsPerMinThreshold =

(int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());

}

}

每注册上一个实例,重新算一下,即

this.expectedNumberOfRenewsPerMin = this.expectedNumberOfRenewsPerMin + 2;

this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());

总的来说,所有实例都注册上了,就是

this.expectedNumberOfRenewsPerMin = N * 2;

this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());

计算例子

2个eureka,算在内,factor=6,N:16

leaseRenewalIntervalInSeconds: 10 --> 每分钟6次

this.expectedNumberOfRenewsPerMin = N * 2 = 32 ;

this.numberOfRenewsPerMinThreshold = (int) (32 * 0.85) = (int)27.2 = 27;

2个eureka,算在内,factor=2,N=3

leaseRenewalIntervalInSeconds: 30 --> 每分钟2次

this.expectedNumberOfRenewsPerMin = N * 2 = 6 ;

this.numberOfRenewsPerMinThreshold = (int) (6* 0.85) = (int)5.1 = 5;

对于单机版eureka的,比较特殊,由于openForTraffic这个方法里头

this.expectedNumberOfRenewsPerMin = count * 2;

这里count=1多算了一个实例的心跳次数,所以如下:

1个eureka,不注册,factor=2,N=1

leaseRenewalIntervalInSeconds: 30 --> 每分钟2次

this.expectedNumberOfRenewsPerMin = N * 2 = 2 ;

this.numberOfRenewsPerMinThreshold = (int) (2 * 0.85) = (int)1.7 = 1;

实际显示是3,应该是按N=2算

this.expectedNumberOfRenewsPerMin = N * 2 = 4 ;

this.numberOfRenewsPerMinThreshold = (int) (4 * 0.85) = (int)3.4 = 3;

1个eureka,不注册,factor=2,N=2

leaseRenewalIntervalInSeconds: 30 --> 每分钟2次

this.expectedNumberOfRenewsPerMin = N * 2 = 4 ;

this.numberOfRenewsPerMinThreshold = (int) (4 * 0.85) = (int)3.4 = 3;

实际显示是5,应该是按N=3算

this.expectedNumberOfRenewsPerMin = N * 2 = 6 ;

this.numberOfRenewsPerMinThreshold = (int) (6 * 0.85) = (int)5.1 = 5;

反思源码

由于eureka-core-1.4.12版本里头,你去调整eureka.instance.leaseRenewalIntervalInSeconds的话,代码里头没有相应调整factor,也就是代码还是60/30=2,所以会破坏eureka内置的设计思路。不过对于小型项目来说,没有跨机房,网络没有那么恶劣的话,想避免自我保护导致的服务注册列表不能修改的问题,可以选择以下任一方式尝试下:

关闭自我保护eureka.server.enableSelfPreservation=false

调小eureka.instance.leaseRenewalIntervalInSeconds,比如设置为10秒

调小renewalPercentThreshold,比如改为0.49

另外,还有一个参数可以调整,就是心跳阈值重新计算的周期:

eureka.server.renewalThresholdUpdateIntervalMs = 15 60 1000 # 15mins

默认是15分钟可以改小一点,比如5分钟=5601000

doc

想获取最新内容,请关注微信公众号

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值