1.源码定位
方法:com.netflix.eureka.resources.InstanceResource#cancelLease
源码版本:v1.7.x
源码地址:https://github.com/Netflix/eureka/tree/v1.7.x
2. 源码解析
protected boolean internalCancel(String appName, String id, boolean isReplication) {
try {
read.lock();
CANCEL.increment(isReplication);
// 根据服务名称从注册表中获取服务实例列表信息
Map<String, Lease<InstanceInfo>> gMap = registry.get(appName);
Lease<InstanceInfo> leaseToCancel = null;
if (gMap != null) {
//移除服务实例
leaseToCancel = gMap.remove(id);
}
synchronized (recentCanceledQueue) {
// 添加到统计队列,最近下线记录队列中
recentCanceledQueue.add(new Pair<Long, String>(System.currentTimeMillis(), appName + "(" + id + ")"));
}
// 服务实例状态表也移除该服务实例信息
InstanceStatus instanceStatus = overriddenInstanceStatusMap.remove(id);
if (instanceStatus != null) {
logger.debug("Removed instance id {} from the overridden map which has value {}", id, instanceStatus.name());
}
if (leaseToCancel == null) {
CANCEL_NOT_FOUND.increment(isReplication);
logger.warn("DS: Registry: cancel failed because Lease is not registered for: {}/{}", appName, id);
return false;
} else {
// 修改下线时间
leaseToCancel.cancel();
InstanceInfo instanceInfo = leaseToCancel.getHolder();
String vip = null;
String svip = null;
if (instanceInfo != null) {
instanceInfo.setActionType(ActionType.DELETED);
// 将服务下线行为添加到增量注册表中,让其他端进行增量同步
recentlyChangedQueue.add(new RecentlyChangedItem(leaseToCancel));
// 服务实例最后修改时间更新
instanceInfo.setLastUpdatedTimestamp();
vip = instanceInfo.getVIPAddress();
svip = instanceInfo.getSecureVipAddress();
}
// 读写缓存过期
invalidateCache(appName, vip, svip);
logger.info("Cancelled instance {}/{} (replication={})", appName, id, isReplication);
return true;
}
} finally {
read.unlock();
}
}
@Override
public boolean cancel(final String appName, final String id,
final boolean isReplication) {
// 服务下线
if (super.cancel(appName, id, isReplication)) {
//成功下线后要同步到其他eureka server节点
replicateToPeers(Action.Cancel, appName, id, null, null, isReplication);
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());
}
}
return true;
}
return false;
}
大概流程:
- 从eureka server的注册表中移除服务实例信息
- 加入到
recentlyChangedQueue
中,做增量下发 - 读写缓存失效
invalidateCache
- 同步到其他节点
replicateToPeers
- 更新自我保护机制核心变量值
expectedNumberOfRenewsPerMin
和numberOfRenewsPerMinThreshold
3.小结
从注册register,心跳发送rewLease,服务实例下线cancel这三个基本说完了。其实总结下来就是只有几点:
- 更新服务实例注册表
registry
- 更新服务实例状态表
overriddenInstanceStatusMap
- 添加到
recentlyChangedQueue
做增量下发 - 读写缓存失效
- 同步到其他eureka server节点