Spring中Eureka的服务下线详解

 更新时间:2023年11月21日 09:42:49   作者:dalianpai  
这篇文章主要介绍了Spring中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;
    }

到此这篇关于Spring中Eureka的服务下线详解的文章就介绍到这了,更多相关Eureka的服务下线内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 浅谈JVM之使用JFR解决内存泄露

    浅谈JVM之使用JFR解决内存泄露

    内存泄露的主要原因就是java中的对象生命周期有长有短。如果长生命周期的对象引用了短生命周期的对象,就有可能造成事实上的内存泄露。本文将介绍JVM之使用JFR解决内存泄露。
    2021-06-06
  • SpringBoot整合mybatis结合pageHelper插件实现分页

    SpringBoot整合mybatis结合pageHelper插件实现分页

    在本篇文章里小编给大家整理的是关于SpringBoot整合mybatis使用pageHelper插件进行分页操作相关知识点,需要的朋友们学习下。
    2020-02-02
  • 用Java打印九九除法表代码分析

    用Java打印九九除法表代码分析

    这篇文章主要介绍了如何用Java语言打印九九除法表,包括其使用的源代码,需要的朋友可以参考下。
    2017-08-08
  • SpringBoot整合EasyExcel实现导入导出数据

    SpringBoot整合EasyExcel实现导入导出数据

    这篇文章主要为大家详细介绍了如何使用Vue、SpringBoot和EasyExcel实现导入导出数据功能,感兴趣的小伙伴可以跟随小编一起学习一下
    2022-05-05
  • SpringBoot后端接收参数优化代码示例(统一处理前端参数)

    SpringBoot后端接收参数优化代码示例(统一处理前端参数)

    使用Spring Boot开发API的时候,读取请求参数是服务端编码中最基本的一项操作,下面这篇文章主要给大家介绍了关于SpringBoot后端接收参数优化(统一处理前端参数)的相关资料,需要的朋友可以参考下
    2024-07-07
  • Spring security登录过程逻辑详解

    Spring security登录过程逻辑详解

    这篇文章主要介绍了SSpringsecurity登录过程逻辑详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • 关于idea-web.xml版本过低怎么生成新的(web.xml报错)问题

    关于idea-web.xml版本过低怎么生成新的(web.xml报错)问题

    今天通过本文给大家分享idea-web.xml版本过低怎么生成新的(web.xml报错)问题,通过更换web.xml版本解决此问题,感兴趣的朋友跟随小编一起看看吧
    2021-07-07
  • Java打包工具jar包详解

    Java打包工具jar包详解

    这篇文章主要介绍了Java打包工具jar包详解,在本例中我们引入一个叫jaxen.jar的库,并将所有以”org.jaxen”开头的类重命名以”org.example.jaxen”开头,具体实例代码跟随小编一起看看吧
    2021-10-10
  • Java基础学习之接口详解

    Java基础学习之接口详解

    接口,是Java语言中一种引用类型,是方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么接口的内部主要就是封装了方法。本文通过一些示例详细为大家展示了接口的使用,需要的可以参考一下
    2022-10-10
  • Java高级应用之斗地主游戏

    Java高级应用之斗地主游戏

    这篇文章主要为大家详细介绍了Java高级应用之斗地主游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-05-05

最新评论