Eureka源码分析--心跳续约机制(4)
前言
对于client端来讲,当注册完了之后,需要维持和server的连接,用来确保本机仍然存活,并且可以被别的client进行远程调用,那么eureka client便有了一个心跳机制。该机制同样会在DiscoveryClient中有一个专门的线程定时去发送。
定时任务的线程启动
在initScheduledTasks() 方法中会构造一个heartbeatTask的线程任务
heartbeatTask = new TimedSupervisorTask(
"heartbeat",
scheduler,
heartbeatExecutor,
renewalIntervalInSecs,
TimeUnit.SECONDS,
expBackOffBound,
new HeartbeatThread()
该线程任务会放入scheduler线程池中,并且默认执行时间为30s钟
线程任务会启动HeartbeatThread类中的run方法
private class HeartbeatThread implements Runnable {
public void run() {
if (renew()) {
lastSuccessfulHeartbeatTimestamp = System.currentTimeMillis();
}
}
}
而在这个方法中核心方法为renew(),在每次成功进行心跳续约后会把最后一次成功续约的时间戳变为当前时间
renew()续约流程
boolean renew() {
EurekaHttpResponse<InstanceInfo> httpResponse;
try {
//构造续约请求的url
httpResponse = eurekaTransport.registrationClient.sendHeartBeat(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null);
logger.debug(PREFIX + "{} - Heartbeat status: {}", appPathIdentifier, httpResponse.getStatusCode());
//如果请求结果为404 则会重新进行注册,这个有可能会发生在网络故障时无法及时发送请求到server,而server长时间
//接收不到本台机子的续约,超过一定时刻server就自动把这个机子下线了,所以当网络恢复的时候再次去请求
//server端的注册表并不存在该台实例,所以就要重新走一次注册流程
if (httpResponse.getStatusCode() == Status.NOT_FOUND.getStatusCode()) {
REREGISTER_COUNTER.increment();
logger.info(PREFIX + "{} - Re-registering apps/{}", appPathIdentifier, instanceInfo.getAppName());
long timestamp = instanceInfo.setIsDirtyWithTime();
boolean success = register();
if (success) {
instanceInfo.unsetIsDirty(timestamp);
}
return success;
}
return httpResponse.getStatusCode() == Status.OK.getStatusCode();
} catch (Throwable e) {
logger.error(PREFIX + "{} - was unable to send heartbeat!", appPathIdentifier, e);
return false;
}
}
server针对续约操作的流程
client发送请求后,会请求到eureka-core模块下的InstanceResource类中的renewLease方法,这个方法是一个put请求。
server端接收到请求后会执行AbstractInstanceRegistry.renew方法
public boolean renew(String appName, String id, boolean isReplication) {
//和监视器有关
RENEW.increment(isReplication);
//获得当前服务名称的所有实例信息
Map<String, Lease<InstanceInfo>> gMap = registry.get(appName);
Lease<InstanceInfo> leaseToRenew = null;
if (gMap != null) {
//根据实例id获得该实例的Lease
leaseToRenew = gMap.get(id);
}
//下面一堆感觉无关紧要的,其实续约的核心就是把Lease中的lastUpdateTimestamp给改变了,就是最下面得一行代码
if (leaseToRenew == null) {
RENEW_NOT_FOUND.increment(isReplication);
logger.warn("DS: Registry: lease doesn't exist, registering resource: {} - {}", appName, id);
return false;
} else {
InstanceInfo instanceInfo = leaseToRenew.getHolder();
if (instanceInfo != null) {
// touchASGCache(instanceInfo.getASGName());
InstanceStatus overriddenInstanceStatus = this.getOverriddenInstanceStatus(
instanceInfo, leaseToRenew, isReplication);
if (overriddenInstanceStatus == InstanceStatus.UNKNOWN) {
logger.info("Instance status UNKNOWN possibly due to deleted override for instance {}"
+ "; re-register required", instanceInfo.getId());
RENEW_NOT_FOUND.increment(isReplication);
return false;
}
if (!instanceInfo.getStatus().equals(overriddenInstanceStatus)) {
logger.info(
"The instance status {} is different from overridden instance status {} for instance {}. "
+ "Hence setting the status to overridden status", instanceInfo.getStatus().name(),
overriddenInstanceStatus.name(),
instanceInfo.getId());
instanceInfo.setStatusWithoutDirty(overriddenInstanceStatus);
}
}
renewsLastMin.increment();
/**
* 续约核心代码!!!!!!!!
* 当前时间+时间间隔
*/
leaseToRenew.renew();
return true;
}
}
总结
整个心跳续约的流程并不难以理解,整个流程简单说就是client发送续约请求,server将对应的实例中的lastUpdateTimestamp更新掉就完了。