Eureka系列(七) 服务下线Server端具体实现

服务下线的大致流程图

  下面这张图很简单地描述了Server端服务下线的大致流程:服务下线.jpg


服务下线Server端实现源码分析

  Eureka服务实现是通过Server端InstanceResource 类 cancelLease 方法来实现服务下线操作,下面我们来看看具体实现代码:

@DELETE
public Response cancelLease(
        @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) {
    try {
        boolean isSuccess = registry.cancel(app.getName(), id,
            "true".equals(isReplication));
        if (isSuccess) {
            logger.debug("Found (Cancel): {} - {}", app.getName(), id);
            return Response.ok().build();
        } else {
            logger.info("Not Found (Cancel): {} - {}", app.getName(), id);
            return Response.status(Status.NOT_FOUND).build();
        }
    } catch (Throwable e) {
        logger.error("Error (cancel): {} - {}", app.getName(), id, e);
        return Response.serverError().build();
    }
}

  由上可见,调用了registry.cancel()方法,我们接着往下看

@Override
public boolean cancel(final String appName, final String id,
                      final boolean isReplication) {
    if (super.cancel(appName, id, isReplication)) {
        // 向其他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;
}

  由上面我们可以发现,如果调用super.cancel(appName, id, isReplication)成功,则会调用replicateToPeers方法向Server端同步发送服务下线消息。我们接着看super.cancel(appName, id, isReplication)的具体实现:

@Override
public boolean cancel(String appName, String id, boolean isReplication) {
    return internalCancel(appName, id, isReplication);
}

  在父类的cancel方法中调用了internalCancel(appName, id, isReplication)来实现,因此我们继续往下看:

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();
    }
}

总结:
  参考上面internalCancel方法的源码,以及之前整体调用的方法,我们可以发现服务续约整体的流程大致可以分为以下几步:
    \(\color{red}{1.删除服务租约信息}\)
    \(\color{red}{2.将下线的服务放进队列里,方便其他Client的增量更新操作}\)
    \(\color{red}{3.删除服务状态信息}\)
    \(\color{red}{4.删除缓存信息}\)(注意:清除的是读写缓存的数据)
    \(\color{red}{5.向其他Server同步发送服务下线消息}\)

posted @ 2020-07-30 10:26  偷吃虾的猫。  阅读(283)  评论(0编辑  收藏  举报