一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情
图解+源码讲解 Eureka Client 心跳机制流程
意志坚强的人能把世界放在手中像泥块一样任意揉捏 —— 歌德 相关文章
eureka-server 项目结构分析
图解+源码讲解 Eureka Server 启动流程分析
图解+源码讲解 Eureka Client 启动流程分析
图解+源码讲解 Eureka Server 注册表缓存逻辑
图解+源码讲解 Eureka Client 拉取注册表流程
图解+源码讲解 Eureka Client 服务注册流程
图解+源码讲解 Eureka Client 心跳机制流程
图解+源码讲解 Eureka Client 下线流程分析
图解+源码讲解 Eureka Server 服务剔除逻辑
图解+源码讲解 Eureka Server 集群注册表同步机制
核心流程图
从哪里开始分析
客户端发送心跳的逻辑指定是客户端初始化的时候进行分析,在它初始化调度任务【initScheduledTasks()】的时候创建了一个30s的线程发送任务线程池,里面每30秒通过renew()发起一次心跳
if (clientConfig.shouldRegisterWithEureka()) {
/**默认30s发送一次心跳*/
int renewalIntervalInSecs = instanceInfo.
getLeaseInfo().getRenewalIntervalInSecs();
int expBackOffBound = clientConfig.
getHeartbeatExecutorExponentialBackOffBound();
// Heartbeat timer
heartbeatTask = new TimedSupervisorTask(
"heartbeat",
scheduler,
heartbeatExecutor,
renewalIntervalInSecs,
TimeUnit.SECONDS,
expBackOffBound,
new HeartbeatThread()
);
/**调度线程池*/
scheduler.schedule(heartbeatTask, renewalIntervalInSecs, TimeUnit.SECONDS);
}
定义了一个 new HeartbeatThread() 任务进行每30s 运行一次,进行心跳请求
心跳请求核心流程
创建核心发送心跳线程任务
private class HeartbeatThread implements Runnable {
public void run() {
// 核心续约方法
if (renew()) {
lastSuccessfulHeartbeatTimestamp = System.currentTimeMillis();
}
}
}
在这里调用了一个叫renew()的方法,该方法是心跳续约的核心方法,如果续约成功的话,那么重置一下最新的心跳成功时间戳为当前的系统时间
心跳续约方法
通过 eurekaTransport.registrationClient 发送心跳请求 sendHeartBeat(),这种请求走的都是 AbstractJersey2EurekaHttpClient的 sendHeartBeat() 方法
boolean renew() {
EurekaHttpResponse<InstanceInfo> httpResponse;
httpResponse = eurekaTransport.registrationClient.sendHeartBeat
(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null);
if (httpResponse.getStatusCode() == Status.NOT_FOUND.getStatusCode()) {
long timestamp = instanceInfo.setIsDirtyWithTime();
boolean success = register();
if (success) {
instanceInfo.unsetIsDirty(timestamp);
}
return success;
}
return httpResponse.getStatusCode() == Status.OK.getStatusCode();
}
发送心跳请求
构造一个发送请求去 eureka-core 项目里面去找InstanceResource 这个类里面的 renewLease 方法这个方法是真正的核心续约请求逻辑
@Override
public EurekaHttpResponse<InstanceInfo> sendHeartBeat(String appName, String id,
InstanceInfo info, InstanceStatus overriddenStatus) {
String urlPath = "apps/" + appName + '/' + id;
Response response = null;
WebTarget webResource = jerseyClient.target(serviceUrl)
.path(urlPath)
.queryParam("status", info.getStatus().toString())
.queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().
toString());
Builder requestBuilder = webResource.request();
addExtraProperties(requestBuilder);
addExtraHeaders(requestBuilder);
requestBuilder.accept(MediaType.APPLICATION_JSON_TYPE);
// put 请求
response = requestBuilder.put(Entity.entity("{}",
MediaType.APPLICATION_JSON_TYPE));
EurekaHttpResponseBuilder<InstanceInfo> eurekaResponseBuilder =
anEurekaHttpResponse(response.getStatus(), InstanceInfo.class).
headers(headersOf(response));
if (response.hasEntity()) {
eurekaResponseBuilder.entity(response.readEntity(InstanceInfo.class));
}
return eurekaResponseBuilder.build();
}
心跳请求处理
核心的请求处理方法是通过注册表的renew方法,PeerAwareInstanceRegistryImpl #renew,这个方法里面就是真正的续约处理逻辑,其实一想就知道是走到注册中心的方法里面去进行处理心跳请求,因为这个是注册中心本地自己维护的一套逻辑,还得同步给其他的服务节点
@PUT
public Response renewLease(
@HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication,
@QueryParam("overriddenstatus") String overriddenStatus,
@QueryParam("status") String status,
@QueryParam("lastDirtyTimestamp") String lastDirtyTimestamp) {
boolean isFromReplicaNode = "true".equals(isReplication);
/**
* 心跳续约
*/
boolean isSuccess = registry.renew(app.getName(), id, isFromReplicaNode);
response = Response.ok().build();
return response;
}
注册表中的renew方法,走到了父类的AbstractInstanceRegistry里面的renew方法,进行续约如果续约成功那么就同步给其他节点进行操作,如果续约失败没找到该要续约节点信息,那么该节点收到结果后就进行发起注册请求
public boolean renew(final String appName, final String id, final boolean isReplication) {
if (super.renew(appName, id, isReplication)) {
replicateToPeers(Action.Heartbeat, appName, id, null, null, isReplication);
return true;
}
return false;
}
在注册表的中获取当前的实例信息,如果不为空的情况下那么就取出来进行心跳续约,如果为空的话那么就返回找不到该实例,让其进行注册,不过这里我们假设能找到,因为我们这里是发送心跳请求逻辑
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) {
leaseToRenew = gMap.get(id);
}
// 省略一些代码
renewsLastMin.increment();
/**
* 心跳续约方法
*/
leaseToRenew.renew();
return true;
}
看看 leaseToRenew.renew(); 这个方法里面做了什么操作,其实就是进行了时间戳的更新操作,duration默认是90s的操作,所以到这里就是心跳续约成功了,所以后续的就是同步给其他的服务节点操作了,同步操作的动作是 Action的 Heartbeat我们后续也会拿出一篇文章进行集群同步讲解
public void renew() {
lastUpdateTimestamp = System.currentTimeMillis() + duration;
}
到此我们就把整个心跳续约的操作进行了讲解
小结
- 客户端初始化的时候创建了心跳请求线程任务
- renew()是真正的心跳续约方法
- 发送心跳请求
- 心跳请求处理
- 心跳集群同步操作,后续会进行讲解
简略图
其实说白了就是注册表中有当前的实例信息,之后每次进行心跳续约的时候查看一下当前的注册表中有没有当前的实例信息,如果有的话那么就进行心跳的时间戳更新操作,如果没有的话那么就进行实例注册操作