美文网首页
Eureka源码分析(七) InstanceResource

Eureka源码分析(七) InstanceResource

作者: skyguard | 来源:发表于2018-11-10 10:45 被阅读0次

    下面我们来分析一下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的分析就到这里了。

    相关文章

      网友评论

          本文标题:Eureka源码分析(七) InstanceResource

          本文链接:https://www.haomeiwen.com/subject/ljfgxqtx.html