Eureka源码分析(七) InstanceResource

2018-11-10  本文已影响0人  skyguard

下面我们来分析一下eureka的下线。应用实例关闭时,Eureka-Client 向 Eureka-Server 发起下线应用实例。需要满足如下条件才可发起:

配置 eureka.registration.enabled = true ,应用实例开启注册开关。默认为 false 。
配置 eureka.shouldUnregisterOnShutdown = true ,应用实例开启关闭时下线开关。默认为 true 。
看一下具体的实现

public synchronized void shutdown() {
    if (isShutdown.compareAndSet(false, true)) {
        logger.info("Shutting down DiscoveryClient ...");

        if (statusChangeListener != null && applicationInfoManager != null) {
            applicationInfoManager.unregisterStatusChangeListener(statusChangeListener.getId());
        }

        cancelScheduledTasks();

        // If APPINFO was registered
        if (applicationInfoManager != null
                && clientConfig.shouldRegisterWithEureka() // eureka.registration.enabled = true
                && clientConfig.shouldUnregisterOnShutdown()) { // eureka.shouldUnregisterOnShutdown = true
            applicationInfoManager.setInstanceStatus(InstanceStatus.DOWN);
            unregister();
        }

        if (eurekaTransport != null) {
            eurekaTransport.shutdown();
        }

        heartbeatStalenessMonitor.shutdown();
        registryStalenessMonitor.shutdown();

        logger.info("Completed shut down of DiscoveryClient");
    }
}

调用unregister方法

void unregister() {
    // It can be null if shouldRegisterWithEureka == false
    if(eurekaTransport != null && eurekaTransport.registrationClient != null) {
        try {
            logger.info("Unregistering ...");
            EurekaHttpResponse<Void> httpResponse = eurekaTransport.registrationClient.cancel(instanceInfo.getAppName(), instanceInfo.getId());
            logger.info(PREFIX + appPathIdentifier + " - deregister  status: " + httpResponse.getStatusCode());
        } catch (Exception e) {
            logger.error(PREFIX + appPathIdentifier + " - de-registration failed" + e.getMessage(), e);
        }
    }
}

调用 AbstractJerseyEurekaHttpClient的cancel方法,DELETE 请求 Eureka-Server 的 apps/APP_NAME/${INSTANCE_INFO_ID} 接口,实现应用实例的下线。
InstanceResource,处理单个应用实例信息的请求操作的 Resource ( Controller )。

下线应用实例信息的请求,映射 InstanceResource的cancelLease方法

@DELETE
public Response cancelLease(
        @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) {
    // 下线
    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();
    }
}

调用 PeerAwareInstanceRegistryImpl的cancel方法,下线应用实例。

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);
        // 【自我保护机制】增加 `numberOfRenewsPerMinThreshold` 、`expectedNumberOfRenewsPerMin`
        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;
}

调用AbstractInstanceRegistry的cancel方法下线应用实例。

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

eureka的下线流程就完成了。
InstanceResource的分析就到这里了。

上一篇下一篇

猜你喜欢

热点阅读