Eureka-Server是如何判断一个服务不可用的?
Eureka是通过心跳续约的方式来检查各个服务提供者的健康状态。
实际上,在判断服务不可用这个部分,会分为两块逻辑。
- Eureka-Server需要定期检查服务提供者的健康状态。
- Eureka-Client在运行过程中需要定期更新注册信息。
Eureka的心跳续约机制如下图所示。
- 客户端在启动时, 会开启一个心跳任务,每隔30s向服务单发送一次心跳请求。
- 服务端维护了每个实例的最后一次心跳时间,客户端发送心跳包过来后,会更新这个心跳时间。
- 服务端在启动时,开启了一个定时任务,该任务每隔60s执行一次,检查每个实例的最后一次心跳时间是否超过90s,如果超过则认为过期,需要剔除。
关于上述流程中涉及到的时间,可以通过以下配置来更改.
#Server 至上一次收到 Client 的心跳之后,等待下一次心跳的超时时间,在这个时间内若没收到下一次心跳,则将移除该 Instance。
eureka.instance.lease-expiration-duration-in-seconds=90
# Server 清理无效节点的时间间隔,默认60000毫秒,即60秒。
eureka.server.eviction-interval-timer-in-ms=60
客户端心跳发起流程#
心跳续约是客户端发起的,每隔30s执行一次。
DiscoveryClient.initScheduledTasks#
继续回到DiscoveryClient.initScheduledTasks
方法中,
private void initScheduledTasks() {
//省略....
heartbeatTask = new TimedSupervisorTask(
"heartbeat",
scheduler,
heartbeatExecutor,
renewalIntervalInSecs,
TimeUnit.SECONDS,
expBackOffBound,
new HeartbeatThread()
);
scheduler.schedule(
heartbeatTask,
renewalIntervalInSecs, TimeUnit.SECONDS);
//省略....
}
renewalIntervalInSecs=30s, 默认每隔30s执行一次。
HeartbeatThread#
这个线程的实现很简单,调用renew()
续约,如果续约成功,则更新最后一次心跳续约时间。
private class HeartbeatThread implements Runnable {
public void run() {
if (renew()) {
lastSuccessfulHeartbeatTimestamp = System.currentTimeMillis();
}
}
}
在renew()
方法中,调用EurekaServer的"apps/" + appName + '/' + id;
这个地址,进行心跳续约。
boolean renew() {
EurekaHttpResponse<InstanceInfo> httpResponse;
try {
httpResponse = eurekaTransport.registrationClient.sendHeartBeat(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null);
logger.debug(PREFIX + "{} - Heartbeat status: {}", appPathIdentifier, httpResponse.getStatusCode());
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;
}
}
服务端收到心跳处理#
服务端具体为调用[com.netflix.eureka.resources]包下的InstanceResource类的renewLease方法进行续约,代码如下
@PUT
public Response renewLease(
@HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication,
@QueryParam("overriddenstatus") String overriddenStatus,
@QueryParam("status") String status,
@QueryParam("lastDirtyTi