图解+源码讲解 Nacos 客户端下线流程

1,804 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第25天,点击查看活动详情

图解+源码讲解 Nacos 客户端下线流程

千里之行,始于足下 —— 老子 Nacos 源码分析系列相关文章

  1. 从零开始看 Nacos 源码环境搭建
  2. 图解+源码讲解 Nacos 客户端发起注册流程
  3. 图解+源码讲解 Nacos 服务端处理注册请求逻辑
  4. 图解+源码讲解 Nacos 客户端下线流程
  5. 图解+源码讲解 Nacos 服务端处理下线请求
  6. 图解+源码讲解 Nacos 客户端发起心跳请求
  7. 图解+源码讲解 Nacos 服务端处理心跳请求
  8. 图解+源码讲解 Nacos 服务端处理配置获取请求

核心思想

    其实要想客户端下线的话,就是将心跳任务停止,不发送心跳给服务端,那么服务端自然而然的就知道客户端已经不好用了,已经死掉了

源码入口

image.png
    这个 destroy方法,在客户端毁掉之前调用了 stop方法 ,那我们就看看这个 stop 方法

服务停止 stop 方法

    一看这个就是实例下线的方法,deregister

public void stop() {
    if (this.getRunning().compareAndSet(true, false) && isEnabled()) {
        // 实例下线方法
        deregister();
        if (shouldRegisterManagement()) {
            deregisterManagement();
        }
        // 关闭一些方法,比如心跳任务、命名空间代理等
        this.serviceRegistry.close();
    }
}

实例下线方法 deregister

protected void deregister() {
    this.serviceRegistry.deregister(getRegistration());
}

    其实调用的就是 NacosServiceRegistry#deregister方法进行服务下线

@Override
public void deregister(Registration registration) {
    log.info("De-registering from Nacos Server now...");

    if (StringUtils.isEmpty(registration.getServiceId())) {
        log.warn("No dom to de-register for nacos client...");
        return;
    }
    // 获取命名空降
    NamingService namingService = namingService();
    // 获取注册中心服务的ID
    String serviceId = registration.getServiceId();
    // 获取组,默认就是 default
    String group = nacosDiscoveryProperties.getGroup();
    // 通过 namingService 进行客户端实例下线
    try {
        namingService.deregisterInstance(serviceId, group, registration.getHost(),
               registration.getPort(), nacosDiscoveryProperties.getClusterName());
    }
    catch (Exception e) {
        log.error("ERR_NACOS_DEREGISTER, de-register failed...{},",
                  registration.toString(), e);
    }
    log.info("De-registration finished.");
}

客户端实例下线 deregisterInstance

    调用的就是 namingServicederegisterInstance方法

@Override
public void deregisterInstance(String serviceName, String groupName, String ip, 
                               int port, String clusterName)
    throws NacosException {
    // 构建一个实例信息
    Instance instance = new Instance();
    instance.setIp(ip);
    instance.setPort(port);
    instance.setClusterName(clusterName);
    // 进行服务下线
    deregisterInstance(serviceName, groupName, instance);
}
@Override
public void deregisterInstance(String serviceName, String groupName, Instance instance)
    throws NacosException {
    // 是否是虚拟节点
    if (instance.isEphemeral()) {
        // 在心跳执行器中移除心跳信息
        beatReactor.removeBeatInfo(NamingUtils.getGroupedName(serviceName, groupName), 
                                   instance.getIp(), instance.getPort());
    }
    // 下线实例
    serverProxy.deregisterService(NamingUtils.getGroupedName(serviceName, groupName),
                                  instance);
}

移除 beatReactor 中心跳信息

    将 beatInfostop 状态设置成true,那么就不会发送心跳了,因为我们分析发送心跳的时候会进行当前心跳信息状态的判断,如果是true那么就不会发送了
image.png
    图中就是发送心跳的判断

public void removeBeatInfo(String serviceName, String ip, int port) {
    NAMING_LOGGER.info("[BEAT] removing beat: {}:{}:{} from beat map.", 
                       serviceName, ip, port);
    // 将心跳信息从 dom2Beat Map集合中移除
    BeatInfo beatInfo = dom2Beat.remove(buildKey(serviceName, ip, port));
    if (beatInfo == null) {
        return;
    }
    // 设置心跳信息停止,在发起心跳的时候会进行判断beatInfo是否是stop的
    beatInfo.setStopped(true);
    // 指标监听
    MetricsMonitor.getDom2BeatSizeMonitor().set(dom2Beat.size());
}

请求服务端下线服务实例

    serverProxy#deregisterService通过这个方法进行服务实例下线

public static String webContext = "/nacos";
public static String nacosUrlBase = webContext + "/v1/ns";
public static String nacosUrlInstance = nacosUrlBase + "/instance";

public void deregisterService(String serviceName, Instance instance) 
        throws NacosException {

    NAMING_LOGGER
        .info("[DEREGISTER-SERVICE] {} deregistering service {} with instance: {}",
              namespaceId, serviceName,instance);
    // 构建访问信息
    final Map<String, String> params = new HashMap<String, String>(8);
    params.put(CommonParams.NAMESPACE_ID, namespaceId);
    params.put(CommonParams.SERVICE_NAME, serviceName);
    params.put(CommonParams.CLUSTER_NAME, instance.getClusterName());
    params.put("ip", instance.getIp());
    params.put("port", String.valueOf(instance.getPort()));
    params.put("ephemeral", String.valueOf(instance.isEphemeral()));
    // 请求服务端进行服务下线,DELETE 方法
    reqApi(UtilAndComs.nacosUrlInstance, params, HttpMethod.DELETE);
}

    所以访问路径是:/nacos/v1/ns/instance

最终关闭 一些后台资源

    关闭一些没有用的资源服务,比如心跳线程池等

@Override
public void close() {
    try {
        // nacos 服务管理下线
        nacosServiceManager.nacosServiceShutDown();
    }
    catch (NacosException e) {
        log.error("Nacos namingService shutDown failed", e);
    }
}

public void nacosServiceShutDown() throws NacosException {
    // 通过命名空间服务关闭一些资源服务
    this.namingService.shutDown();
    namingService = null;
    namingMaintainService = null;
}
// 关闭一些相关的资源服务
@Override
public void shutDown() throws NacosException {
    beatReactor.shutdown();
    hostReactor.shutdown();
    serverProxy.shutdown();
}

小结

    就是将客户端的心跳服务关闭,之后访问服务端进行客户端端下线请求,最终再关闭一些资源服务

其他系列源码分析

feign 源码分析系列相关文章

  1. 图解+源码讲解 Feign 如何将客户端注入到容器中
  2. 图解+源码讲解动态代理获取 FeignClient 代理对象
  3. 图解+源码讲解代理对象 ReflectiveFeign 分析
  4. 图解+源码讲解 Feign 如何选取指定服务
  5. 图解+源码讲解 Feign 请求的流程
    ribbon 源码分析系列相关文章
  6. Ribbon 原理初探
  7. 图解+源码讲解 Ribbon 如何获取注册中心的实例
  8. 图解+源码讲解 Ribbon 服务列表更新
  9. 图解+源码讲解 Ribbon 服务选择原理
  10. 图解+源码讲解 Ribbon 如何发起网络请求 eureka 源码分析系列相关文章
  11. eureka-server 项目结构分析
  12. 图解+源码讲解 Eureka Server 启动流程分析
  13. 图解+源码讲解 Eureka Client 启动流程分析
  14. 图解+源码讲解 Eureka Server 注册表缓存逻辑
  15. 图解+源码讲解 Eureka Client 拉取注册表流程
  16. 图解+源码讲解 Eureka Client 服务注册流程
  17. 图解+源码讲解 Eureka Client 心跳机制流程
  18. 图解+源码讲解 Eureka Client 下线流程分析
  19. 图解+源码讲解 Eureka Server 服务剔除逻辑
  20. 图解+源码讲解 Eureka Server 集群注册表同步机制