zoukankan      html  css  js  c++  java
  • Eureka 下线 天宇轩

    Eureka 下线

    // DiscoveryClient.java
       @PreDestroy
        @Override
        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()
                        && clientConfig.shouldUnregisterOnShutdown()) {
                    //讲服务实例的状态设置为DOWN
                    applicationInfoManager.setInstanceStatus(InstanceStatus.DOWN);
                    //取消注册
                    unregister();
                }
    
                //关闭网络通信组件
                if (eurekaTransport != null) {
                    eurekaTransport.shutdown();
                }
    
                //关闭监听器
                heartbeatStalenessMonitor.shutdown();
                registryStalenessMonitor.shutdown();
    
                Monitors.unregisterObject(this);
    
                logger.info("Completed shut down of DiscoveryClient");
            }
        }
    
    • 调用 ApplicationInfoManager#setInstanceStatus(...) 方法,设置应用实例为关闭( DOWN )。
    • 调用 #unregister() 方法,实现代码如下:
    // DiscoveryClient.java
    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.java
    @Override
    public EurekaHttpResponse<Void> cancel(String appName, String id) {
        String urlPath = "apps/" + appName + '/' + id;
        ClientResponse response = null;
        try {
            Builder resourceBuilder = jerseyClient.resource(serviceUrl).path(urlPath).getRequestBuilder();
            addExtraHeaders(resourceBuilder);
            response = resourceBuilder.delete(ClientResponse.class);
            return anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build();
        } finally {
            if (logger.isDebugEnabled()) {
                logger.debug("Jersey HTTP DELETE {}/{}; statusCode={}", serviceUrl, urlPath, response == null ? "N/A" : response.getStatus());
            }
            if (response != null) {
                response.close();
            }
        }
    }
    
    • 调用 AbstractJerseyEurekaHttpClient#cancel(...) 方法,DELETE 请求 Eureka-Server 的 apps/${APP_NAME}/${INSTANCE_INFO_ID} 接口,实现应用实例信息的下线。

    com.netflix.eureka.resources.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();
       }
    }
    

    调用 AbstractInstanceRegistry#cancel(...) 方法,下线应用实例信息,实现代码如下:

      @Override
        public boolean cancel(String appName, String id, boolean isReplication) {
            return internalCancel(appName, id, isReplication);
        }
    
        /**
         * {@link #cancel(String, String, boolean)} method is overridden by {@link PeerAwareInstanceRegistry}, so each
         * cancel request is replicated to the peers. This is however not desired for expires which would be counted
         * in the remote peers as valid cancellations, so self preservation mode would not kick-in.
         */
        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);
                }
    
                //将服务实例加入最近下线的queue中
                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 {
                    //调用cancel,保存一个时间戳
                    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();
    
                        //服务的注册,下线,过期,都会代表这个服务实例变化了,都会将自己放入最近改变的队列中
                        //这个最近改变的队列,只会保留最近3分钟的实例
                        //所以说eureka client拉取增量注册表的时候,其实就是拉取最近3分钟有变化的服务实例。
                    }
                    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;
        }
    
  • 相关阅读:
    android ListView布局之一(继承listActivity、使用arrayAdapter)
    android your project contains error
    wojilu系统的ORM代码解析[源代码结构分析,ObjectBase基类分析]
    ORM中启用数据库事务
    我记录网站综合系统 技术原理解析[11:ActionProcessor流程wojilu核心]
    互联网,让我们更安全了,还是更危险了【纯讨论】
    不用服务器也能跑的框架wojilu续篇
    使用wojilu 无代码实现 输入框提示 及其背后的原理
    wojilu日志系统可以单独使用
    “我有什么” 和 “你要什么” 框架制作的一些思考
  • 原文地址:https://www.cnblogs.com/dalianpai/p/15395746.html
Copyright © 2011-2022 走看看