原创不易,转载请注明出处
前言
服务续约可以理解为心跳,客户端服务需要定时的向Eureka Server 发送续约请求,告诉Eureka Server 我这服务还活着,你别把我这个服务信息从注册表中剔除了。其实就是找到这个客户端实例对应的instance租约信息,更新一下最后更新时间。Eureka Server 有个后台任务,定时的去扫描注册表中的实例租约信息,看看更新时间与现在时间是不是隔了超过存活时间(默认是90s),如果超过了这个时间,就会将这个服务实例信息从注册表中剔除。
1.源码解析
客户端服务调用apps/{appName}/{instanceId} 路径发送续约请求。
Eureka Server接收服务续约请求是在InstanceResource 的renewLease 方法进行的。
@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);
// 此处省略若干行代码
return response;
}
可以看到,调用了registry 注册表的renew 方法,参数1:appName ,参数2 instanceId,参数3,是否是服务集群其他节点同步请求,这个是与集群间同步相关的参数。
我们看看注册表PeerAwareInstanceRegistryImpl 的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;
}
分为2步,1是调用父类AbstractInstanceRegistry 的renew 方法,变更本地注册表服务实例租约信息,2是调用replicateToPeers 方法,将服务续约请求同步给集群的其他节点。这里我们只看下第一步的内容。
public boolean renew(String appName, String id, boolean isReplication) {
RENEW.increment(isReplication);
//根据appName 找到对应的实例租约列表
Map<String, Lease<InstanceInfo>> gMap = registry.get(appName);
Lease<InstanceInfo> leaseToRenew = null;
if (gMap != null) {
// 通过实例id (instanceId 获取对应实例的租约信息)
leaseToRenew = gMap.get(id);
}
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);
}
}
// renew计数,自我保护机制会用到
renewsLastMin.increment();
// 实例
leaseToRenew.renew();
return true;
}
}
这段代码核心流程就是通过 appName 与instanceId 实例id 获取对应实例的租约信息,然后就是 renewsLastMin.increment();
增加renew 计数这个后面的自我保护机制会用到, 最后是调用租约信息的renew 方法进行实例续约,也就是执行leaseToRenew.renew()
这行代码,return true。
我们来看下续约信息的renew方法。
public void renew() {
lastUpdateTimestamp = System.currentTimeMillis() + duration;
}
其实就是更新lastUpdateTimestamp 这个字段。
将lastUpdateTimestamp 设置成 当前时间+ 过期间隔, 如果说一旦lastUpdateTimestamp 小于了当前时间,就说明这个实例租约过期了,这个实例可能会被Eureka Server 后台线程从注册表中剔除。
2.流程图
总结
本文主要是解析了一下Eureka Server处理服务续约请求的流程代码,当客户端进行服务注册之后,会有一个定时任务,默认是每30s 向Eureka Server端进行服务续约,也就是告诉Eureka Server 我这个客户端当前还活着,别给我从注册表中剔除了,服务续约其实就是心跳,如果是一段时间没有向Eureka Server 发送续约请求,就会被认为服务发生故障,故而从注册表中剔除。