Nacos 服务注册概述及客户端注册实例源码分析(一)(上)

简介: Nacos 服务注册概述及客户端注册实例源码分析(一)(上)

Nacos 服务注册与发现概述

Nacos 核心功能点

服务注册: Nacos Client 会通过发送 REST 请求的方式向 Nacos Server 注册自己的服务,提供自身的元数据,比如 IP 地址、端口等信息,Nacos Server 接收到注册请求以后,就会把这些元数据信息存储在一个双层的内存 Map 中

服务心跳: 在服务注册后,Nacos Client 会维护一个定时心跳来持续通知 Nacos Server,说明服务一直处于可用状态,防止被剔除,默认 5s 发送一次心跳

服务健康检查: Nacos Server 会开启一个定时任务来检查注册服务实例的健康情况,对于超过 15s 没有收到客户端心跳会将它的 healthy 属性设置为 false「客户端服务发现时不会发现」,如果某个实例超过 30s 没有收到心跳,直接剔除该实例「被剔除的实例如果恢复发送心跳则会重新注册」

服务发现: 服务消费者(Nacos Client)在调用服务提供者的服务时,会发送一个 REST 请求给 Nacos Server,获取上面注册的服务清单,并且缓存在 Nacos Client 本地,同时会在 Nacos Client 本地开启一个定时任务来定时拉取服务端最新的注册表信息更新到本地缓存中

服务同步: Nacos Server 集群之间会互相同步服务实例,来保证服务信息的一致性

Nacos 服务端及客户端模块图

如上图,核心模块集中在 nacos-console、name-naming、nacos-config 中

Nacos 客户端服务注册源码入口分析

Nacos GitHub 地址

Nacos 源码,本文在 nacos-2.1.1 版本进行分析

服务注册信息

从 nacos-client 模块中开始说起,说起客户端就必然涉及到服务注册,先了解一下 Nacos 客户端会传递什么信息给到服务端侧,我们直接从 nacos-client 项目的 NamingTest 类说起:

public class NamingTest {
    @Test
    public void testServiceList() throws Exception {
        Properties properties = new Properties();
        properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1:8848");
        properties.put(PropertyKeyConst.USERNAME, "nacos");
        properties.put(PropertyKeyConst.PASSWORD, "nacos");
        Instance instance = new Instance();
        instance.setIp("1.1.1.1");
        instance.setPort(800);
        instance.setWeight(2);
        Map<String, String> map = new HashMap<String, String>();
        map.put("netType", "external");
        map.put("version", "2.0");
        instance.setMetadata(map);
        NamingService namingService = NacosFactory.createNamingService(properties);
        namingService.registerInstance("nacos.test.1", instance);
        ThreadUtils.sleep(5000L);
        List<Instance> list = namingService.getAllInstances("nacos.test.1");
        System.out.println(list);
        ThreadUtils.sleep(30000L);
        //        ExpressionSelector expressionSelector = new ExpressionSelector();
        //        expressionSelector.setExpression("INSTANCE.metadata.registerSource = 'dubbo'");
        //        ListView<String> serviceList = namingService.getServicesOfServer(1, 10, expressionSelector);
    }
}

其实这就是客户端注册的一个 Test 类,它模仿了一个真实的服务注册进了 Nacos 的过程,包括 NacosServer 连接、实例的创建、实例属性的赋值、注册实例,所以在这个其中包括了服务注册的核心代码;仅从此处的代码分析,可以看出,Nacos 注册服务实例时,包含了两大类信息:Nacos Server 连接信息和实例信息

Nacos Server 连接信息

Nacos Server 连接信息,存储在 Properties 当中,包含以下信息:

  • Server 地址:Nacos 服务器地址,属性 Key 为 serverAddr
  • 用户名:连接 Nacos 服务用户名,属性 Key 为 username,默认值为 nacos
  • 密码:连接 Nacos 服务密码,属性 Key 为 password,默认值为 nacos

实例信息

注册实例信息用 Instance 对象承载,注册的实例信息又分为两部分:实例基础信息、元数据

实例基础信息

  • instanceId:实例的唯一 ID
  • ip:实例 IP,提供给消费者进行通信的地址
  • port:端口,提供给消费者访问的端口
  • weight:权重,当前实例的权限,浮点类型(默认为 1.0D)
  • healthy:健康状况,默认 true
  • enabled:实例是否准备好接收请求,默认 true
  • ephemeral:实例是否为瞬时的,默认为 true,缓存在内存中,还未持久化入库的
  • clusterName:实例所属的集群名称
  • serviceName:实例的服务信息

Instance 类包含了实例的基础信息之外,还包含了用于存储元数据的 metadata「描述数据的数据」类型为 HashMap,从当前这个 Demo 中我们可以得知存放了两个数据:

  • netType:顾名思义,网络类型:值为 external,也就是外网的意思
  • version:版本,Nacos 版本,这里是 2.0 版本

除了 Demo 中这些 “自定义” 信息,在 Instance 类中还定义了一些默认信息,这些信息通过 get 方法提供:

// 心跳的间隔时间默认值 5s
public long getInstanceHeartBeatInterval() {
  return getMetaDataByKeyWithDefault(PreservedMetadataKeys.HEART_BEAT_INTERVAL,
                                     Constants.DEFAULT_HEART_BEAT_INTERVAL);
}
// 心跳超时时间默认值 15s
public long getInstanceHeartBeatTimeOut() {
  return getMetaDataByKeyWithDefault(PreservedMetadataKeys.HEART_BEAT_TIMEOUT,
                                     Constants.DEFAULT_HEART_BEAT_TIMEOUT);
}
// IP 删除超时时间默认值 30s
public long getIpDeleteTimeout() {
  return getMetaDataByKeyWithDefault(PreservedMetadataKeys.IP_DELETE_TIMEOUT,
                                     Constants.DEFAULT_IP_DELETE_TIMEOUT);
}
// 实例 ID 生成器默认值:simple
public String getInstanceIdGenerator() {
  return getMetaDataByKeyWithDefault(PreservedMetadataKeys.INSTANCE_ID_GENERATOR,
                                     Constants.DEFAULT_INSTANCE_ID_GENERATOR);
}

上面的 get 方法在需要元数据默认值时会被使用到:

  • preserved.heart.beat.interval:心跳间隔 Key,默认值为 5s,也就是默认 5s 进行一次心跳
  • preserved.heart.beat.timeout:心跳超时 Key,默认值为 15s,也就是默认 15s 收不到心跳,实例将会标记为不健康
  • preserved.ip.delete.timeout:实例 IP 被删除 Key,默认值为 30s,也就是 30s 收不到心跳,实例将会被移除
  • preserved.instance.id.generator:实例 ID 生成器 Key,默认值为 simple

这些都是 Nacos 提供的默认值,也就是当前实例注册时会告知 Nacos Server 说:我的心跳间隔、心跳超时等对应的值是多少,按照这个值来判断我这个实例是否健康

有了这些信息,基本上已经知道注册实例时需要传递什么参数、需要配置什么参数了

NamingService 接口

NamingService 接口是 Nacos 命名服务对外提供的一个统一接口,看对应的源码可以发现,它提供了大量实例相关的接口方法

  • 注册服务实例,提供了多个不同参数的重载方法,可以指定组名、集群名
// 注册服务实例,指定 IP、Port
void registerInstance(String serviceName, String ip, int port) throws NacosException
  • 注销服务实例
void deregisterInstance(String serviceName, String ip, int port) throws NacosException
  • 获取全部的服务实例
List<Instance> getAllInstances(String serviceName) throws NacosException
  • 获取健康的服务实例
List<Instance> selectInstances(String serviceName, boolean healthy) throws NacosException
  • 获取集群中健康的服务实例
List<I
  • 使用负载均衡策略选择一个健康的服务实例
Instance selectOneHealthyInstance(String serviceName) throws NacosException
  • 订阅服务事件
void subscribe(String serviceName, EventListener listener) throws NacosException
  • 取消订阅服务事件
void unsubscribe(String serviceName, EventListener listener) throws NacosException
  • 获取所有(或指定)服务名称
ListView<String> getServicesOfServer(int pageNo, int pageSize, ...) throws NacosException
  • 获取所有订阅的服务
List<ServiceInfo> getSubscribeServices() throws NacosException
  • 获取 Nacos 服务状态
String getServerStatus()
  • 主动关闭服务
void shutDown() throws NacosException

这些方法中提供了大量的重载方法,应用于不同场景、不同类型实例或服务的筛选,所以我们只需要在不同的情况下使用不同的方法即可

NamingService 实例化是通过 NamingFactory 类和上面的 Nacos 服务信息,从以下代码中可以看出这里采用了反射机制来实例化 NamingService,具体的实现类为 NacosNamingService:

public static NamingService createNamingService(Properties properties) throws NacosException {
  try {
    Class<?> driverImplClass = Class.forName("com.alibaba.nacos.client.naming.NacosNamingService");
    Constructor constructor = driverImplClass.getConstructor(Properties.class);
    return (NamingService) constructor.newInstance(properties);
  } catch (Throwable e) {
    throw new NacosException(NacosException.CLIENT_INVALID_PARAM, e);
  }
}

NacosNamingService 实现

在示例代码中使用了 NamingService#registerInstance 方法来进行服务实例的注册,该方法接收两个参数:服务名称和实例对象;这个方法的最大作用是设置了当前实例的分组信息;在 Nacos 中,通过 Namespace、Group、Service、Cluster 等一层层的将实例进行环境的隔离;在这里设置了默认的分组名:DEFAULT_GROUP

public void registerInstance(String serviceName, Instance instance) throws NacosException {
  registerInstance(serviceName, Constants.DEFAULT_GROUP, instance);
}

紧接着调用的 registerInstance 方法如下,这个方法做了两件事情:

public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
  NamingUtils.checkInstanceIsLegal(instance);
  clientProxy.registerService(serviceName, groupName, instance);
}

1、检查心跳时间设置的是否正确(心跳默认值是 5s)

public static void checkInstanceIsLegal(Instance instance) throws NacosException {
  // 实例的心跳间隔必须小于 "心跳超时" 和 "ip删除超时"
  if (instance.getInstanceHeartBeatTimeOut() < instance.getInstanceHeartBeatInterval()
      || instance.getIpDeleteTimeout() < instance.getInstanceHeartBeatInterval()) {
    throw new NacosException(NacosException.INVALID_PARAM,
                             "Instance 'heart beat interval' must less than 'heart beat timeout' and 'ip delete timeout'.");
  }
  // 实例的集群名称不满足条件:只支持数字和字母
  if (!StringUtils.isEmpty(instance.getClusterName()) && !CLUSTER_NAME_PATTERN.matcher(instance.getClusterName()).matches()) {
    throw new NacosException(NacosException.INVALID_PARAM,String.format("Instance 'clusterName' should be characters with only 0-9a-zA-Z-. (current: %s)",
                                           instance.getClusterName()));
  }
}

2、通过 NamingClientProxy 代理类来执行服务注册操作

通过 clientProxy 属性可以发现 NamingClientProxy 这个代理接口的具体实现是由 NamingClientProxyDelegate 来完成的,这个可以直接从 NacosNamingService 构造方法看出,在 init 方法中进行初始化操作:

public NacosNamingService(Properties properties) throws NacosException {
  init(properties);
}
private void init(Properties properties) throws NacosException {
  ValidatorUtils.checkInitParam(properties);
  this.namespace = InitUtils.initNamespaceForNaming(properties);
  InitUtils.initSerialization();
  InitUtils.initWebRootContext(properties);
  initLogName(properties);
  this.notifierEventScope = UUID.randomUUID().toString();
  this.changeNotifier = new InstancesChangeNotifier(this.notifierEventScope);
  NotifyCenter.registerToPublisher(InstancesChangeEvent.class, 16384);
  NotifyCenter.registerSubscriber(changeNotifier);
  this.serviceInfoHolder = new ServiceInfoHolder(namespace, this.notifierEventScope, properties);
  this.clientProxy = new NamingClientProxyDelegate(this.namespace, serviceInfoHolder, properties, changeNotifier);
}

vnjohn
+关注
目录
打赏
0
1
1
0
241
分享
相关文章
Nacos作为流行的微服务注册与配置中心,其稳定性与易用性广受好评
Nacos作为流行的微服务注册与配置中心,其稳定性与易用性广受好评。然而,“客户端不发送心跳检测”是使用中常见的问题之一。本文详细探讨了该问题的原因及解决方法,包括检查客户端配置、网络连接、日志、版本兼容性、心跳检测策略、服务实例注册状态、重启应用及环境变量等步骤,旨在帮助开发者快速定位并解决问题,确保服务正常运行。
72 5
阿里云的 Dubbo 和 Nacos 深度整合,提供了高效的服务注册与发现、配置管理等关键功能,简化了微服务治理,提升了系统的灵活性和可靠性。
在云原生时代,微服务架构成为主流。阿里云的 Dubbo 和 Nacos 深度整合,提供了高效的服务注册与发现、配置管理等关键功能,简化了微服务治理,提升了系统的灵活性和可靠性。示例代码展示了如何在项目中实现两者的整合,通过 Nacos 动态调整服务状态和配置,适应多变的业务需求。
116 2
Nacos作为流行的微服务注册与配置中心,“节点提示暂时不可用”是常见的问题之一
Nacos作为流行的微服务注册与配置中心,其稳定性和易用性备受青睐。然而,“节点提示暂时不可用”是常见的问题之一。本文将探讨该问题的原因及解决方案,帮助开发者快速定位并解决问题,确保服务的正常运行。通过检查服务实例状态、网络连接、Nacos配置、调整健康检查策略等步骤,可以有效解决这一问题。
55 4
Nacos作为流行的微服务注册与配置中心,其稳定性和易用性备受青睐。
Nacos作为流行的微服务注册与配置中心,其稳定性和易用性备受青睐。然而,实际使用中常遇到“客户端不发送心跳检测”的问题。本文深入探讨该问题的原因及解决方案,帮助开发者快速定位并解决问题,确保服务正常运行。通过检查客户端配置、网络连接、日志、版本兼容性、心跳策略、注册状态、重启应用和环境变量等步骤,系统地排查和解决这一问题。
73 3
Nacos是一款流行的微服务注册与配置中心,但直接暴露在公网中可能导致非法访问和数据库篡改
Nacos是一款流行的微服务注册与配置中心,但直接暴露在公网中可能导致非法访问和数据库篡改。本文详细探讨了这一问题的原因及解决方案,包括限制公网访问、使用HTTPS、强化数据库安全、启用访问控制、监控和审计等步骤,帮助开发者确保服务的安全运行。
156 3
高效搭建Nacos:实现微服务的服务注册与配置中心
Nacos(Dynamic Naming and Configuration Service)是阿里巴巴开源的一款动态服务发现、配置管理和服务管理平台。它旨在帮助开发者更轻松地构建、部署和管理分布式系统,特别是在微服务架构中。
497 81
高效搭建Nacos:实现微服务的服务注册与配置中心
SpringCloud 应用 Nacos 配置中心注解
在 Spring Cloud 应用中可以非常低成本地集成 Nacos 实现配置动态刷新,在应用程序代码中通过 Spring 官方的注解 @Value 和 @ConfigurationProperties,引用 Spring enviroment 上下文中的属性值,这种用法的最大优点是无代码层面侵入性,但也存在诸多限制,为了解决问题,提升应用接入 Nacos 配置中心的易用性,Spring Cloud Alibaba 发布一套全新的 Nacos 配置中心的注解。
334 18
使用 nacos 搭建注册中心及配置中心
使用 nacos 搭建注册中心及配置中心
129 5
|
7月前
|
SpringCloud集成Seata并使用Nacos做注册中心与配置中心
SpringCloud集成Seata并使用Nacos做注册中心与配置中心
261 3
Nacos 配置中心变更利器:自定义标签灰度
本文是对 MSE Nacos 应用自定义标签灰度的功能介绍,欢迎大家升级版本进行试用。
532 15

热门文章

最新文章