服务下线
```java
/**
* @Description: 服务下线
* @Author: PABLO
* @Date: 2022/5/18 15:27
* @Params: [appName, id, isReplication]
* @Return: boolean
**/
protected boolean internalCancel(String appName, String id, boolean isReplication) {
read.lock();
try {
CANCEL.increment(isReplication);
//获取对应集群
Map<String, Lease<InstanceInfo>> gMap = registry.get(appName);
Lease<InstanceInfo> leaseToCancel = null;
if (gMap != null) {
//删除对应节点信息
leaseToCancel = gMap.remove(id);
}
recentCanceledQueue.add(new Pair<Long, String>(System.currentTimeMillis(), appName + "(" + id + ")"));
InstanceInfo.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) {
//状态更改delete
instanceInfo.setActionType(InstanceInfo.ActionType.DELETED);
recentlyChangedQueue.add(new AbstractInstanceRegistry.RecentlyChangedItem(leaseToCancel));
instanceInfo.setLastUpdatedTimestamp();
vip = instanceInfo.getVIPAddress();
svip = instanceInfo.getSecureVipAddress();
}
//删除缓存
invalidateCache(appName, vip, svip);
logger.info("Cancelled instance {}/{} (replication={})", appName, id, isReplication);
}
} finally {
read.unlock();
}
synchronized (lock) {
if (this.expectedNumberOfClientsSendingRenews > 0) {
// Since the client wants to cancel it, reduce the number of clients to send renews.
this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews - 1;
updateRenewsPerMinThreshold();
}
}
return true;
}
服务剔除
/**
* @Description: 服务剔除
* @Author: PABLO
* @Date: 2022/5/18 15:56
* @Params: [additionalLeaseMs]
* @Return: void
**/
public void evict(long additionalLeaseMs) {
logger.debug("Running the evict task");
//判断自我保护机制,如打开不可剔除,如关闭自我保护,心跳检测不到的节点就会被剔除
//自我保护机制的目的是为了避免误杀(如网络原因导致心跳超时),这样正常的服务将不会被访问到
//但如自身出现故障,将接受不到节点的心跳,导致服务瘫痪
//可设置阈值控制自我保护机制的是否开启,如到达阈值将不会剔除任何节点,当超过阈值,才恢复正常状态,剔除心跳检测不到的节点
//阈值默认0.85,该阈值是所有微服务节点数量下界的阈值,如100台机器,宕机15+,就会触发自我保护
if (!isLeaseExpirationEnabled()) {
logger.debug("DS: lease expiration is currently disabled.");
return;
}
//获取所有过期的节点(租约),我们的节点被Lease租约对象所包含,控制租约相关事宜
List<Lease<InstanceInfo>> expiredLeases = new ArrayList<>();
//遍历注册表
for (Map.Entry<String, Map<String, Lease<InstanceInfo>>> groupEntry : registry.entrySet()) {
Map<String, Lease<InstanceInfo>> leaseMap = groupEntry.getValue();
if (leaseMap != null) {
for (Map.Entry<String, Lease<InstanceInfo>> leaseEntry : leaseMap.entrySet()) {
Lease<InstanceInfo> lease = leaseEntry.getValue();
//判断租约是否过期
if (lease.isExpired(additionalLeaseMs) && lease.getHolder() != null) {
expiredLeases.add(lease);
}
}
}
}
//避免大量下线,判断阈值
//获取当前注册表大小
int registrySize = (int) getLocalRegistrySize();
//计算阈值对应的机器数量 * 0.85 默认
int registrySizeThreshold = (int) (registrySize * serverConfig.getRenewalPercentThreshold());
//总数-阈值对应的值 ,用于判断当前过期节点(即将操作下线的节点的个数)和阈值控制的节点个数
//自我保护默认开启,真正开启自我保护是接受客户端心跳未达到预期阈值,server进入自我保护后客户端服务不一定会剔除
int evictionLimit = registrySize - registrySizeThreshold;
int toEvict = Math.min(expiredLeases.size(), evictionLimit);
if (toEvict > 0) {
logger.info("Evicting {} items (expired={}, evictionLimit={})", toEvict, expiredLeases.size(), evictionLimit);
Random random = new Random(System.currentTimeMillis());
for (int i = 0; i < toEvict; i++) {
//随机下线
int next = i + random.nextInt(expiredLeases.size() - i);
Collections.swap(expiredLeases, i, next);
Lease<InstanceInfo> lease = expiredLeases.get(i);
String appName = lease.getHolder().getAppName();
String id = lease.getHolder().getId();
EXPIRED.increment();
logger.warn("DS: Registry: expired lease for {}/{}", appName, id);
//剔除
internalCancel(appName, id, false);
}
}
}