Nacos源码阅读开篇之Nacos客户端-服务注册信息和NamingService接口


1、Nacos 注册信息

以源码中自带的ManingTest 测试类距离举例,
这是客户端注册的一个测试类,他模仿了一个真实的服务注册进Naocos 的过程,包括NacosServer连接、实例的创建、实力属性的赋值、注册实例,所以在这个其中包含了服务注册的核心代码,可以看出,Nacos注册服务实例时,包含了两大类信息: Nacos Server 连接信息 和 实例信息。
在这里插入图片描述
内容如下:

@Ignore
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);
        
    }

1.1、Nacos Server 连接信息

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

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

1.2、实例信息

  • InstanceId:实例的唯一ID
  • Ip:实例IP,提供给消费者进行通讯的地址
  • port:端口,提供给消费者访问的端口
  • weight:权重,当前实例的权限,浮点类型(1.0D)
  • enabled:实例是否准备好接收请求,默认true;
  • ephemeral:实例所属的机群名称;
  • clusterName: 实例所属的机群名称;
  • servlceName:实例的服务信息;

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

  • netType:故名思义,网络类型,这里的值为extenal,也就是外网的意思;
  • version:版本,Nacos的版本,这里是2.0这个大版本

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

#类 --> com.alibaba.nacos.api.naming.pojo.Instance;
# 获取一次心跳的间隙 默认是5public long getInstanceHeartBeatInterval() {
        return getMetaDataByKeyWithDefault(PreservedMetadataKeys.HEART_BEAT_INTERVAL,
                Constants.DEFAULT_HEART_BEAT_INTERVAL);
    }
  # 获取实例的心跳超时时间 默认是15public long getInstanceHeartBeatTimeOut() {
        return getMetaDataByKeyWithDefault(PreservedMetadataKeys.HEART_BEAT_TIMEOUT,
                Constants.DEFAULT_HEART_BEAT_TIMEOUT);
    }
# 获取实例的超时铲除时间 默认是30public long getIpDeleteTimeout() {
        return getMetaDataByKeyWithDefault(PreservedMetadataKeys.IP_DELETE_TIMEOUT,
                Constants.DEFAULT_IP_DELETE_TIMEOUT);
    }
# 获取实例的 实际ID
    public String getInstanceIdGenerator() {
        return getMetaDataByKeyWithDefault(PreservedMetadataKeys.INSTANCE_ID_GENERATOR,
                Constants.DEFAULT_INSTANCE_ID_GENERATOR);
    }

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

  • PreservedMetadataKeys.HEART_BEAT_INTERVAL:心跳间隙的key,默认为5s,也就是默认5s 进行一次心跳;
  • PreservedMetadataKeys.HEART_BEAT_TIMEOUT:心跳超时的key,默认为15s,也就是默认15s收不到心跳,实例将会标记为不健康;
  • PreservedMetadataKeys.IP_DELETE_TIMEOUT: 实例IP被删除的key,默认为30s,也就是30s收不到心跳,实例将会被移除;
  • PreservedMetadataKeys.INSTANCE_ID_GENERATOR: 实例ID 生成器key,默认为simple;
    这些都是Nacos默认提供的值,也就是当前用力注册时会高数Nacos Server说:我的心跳间隙,心跳超时等对应的值是多少,你按照这个值来判断我这个实例是否健康。

有了这些信息,我们疾病是已经知道注册实例是需要传递什么参数,需要配置什么参数了。


1.3、NamingService接口

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

  • 服务实例注册

void regsterInstance(…) throws NacosException;

  • 服务实例注销

void deregisterInstance(…) throws NacosExcetion;

  • 获取服务实例

List getAllInstances(…) throws NacosExcetion;

  • 查询健康服务实例

List selectInstances(…) throws NacosExcetion;

  • 查询集群中健康的服务实例

List selectInstance(…List clusters …) throws NacosExcetion;

  • 使用负载均衡策略选择一个将康的服务实例

Instance selectOneHealthyInstance(…) throws NacosExcetion;

  • 订阅服务时间

void subscribe(…) throws NacosExcetion;

  • 取消订阅服务事件

void unsubscribe(…) throws NacosExcetion;

  • 获取所有(或指定)服务名称

ListView getServiceOfServer(…) throws NacosExcetion;

  • 获取Nacos服务的状态

String getServiceStatus();

  • 主动关闭服务

void shutDown() throws NacosExcetion;

在这些方法中提供了大量的重载方法,应用于不同类型实例和服务的筛选,所以我们只需要在不同的情况下使用不同的方法即可。
NamingService的实例画是通过NamingFactory类和上面的Nacos服务信息,从代码中可以看出这里采用的反射机制来市里话NamingService,具体的实现类为NacosNamingService:

#类 com.alibaba.nacos.api.naming.NamingFactory;

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);
        }
    }

1.4、 NacosNamingService的实现

在示例代码中使用了NamingService的registerInstance方法来进行服务实现的注册,该方法接收两个参数、服务名称和实力对象。这个方法的最大作用是设置了当前实例的分组信息。我们知道,在Nacos中,通过Namespace、group、Service、Cluster等一层层的将实例进行环境的隔离。在这里设置了默认的分组为“DEFAULT_GROUP”。
#com.alibaba.nacos.client.naming.NacosNamingService;
#num = 137 row
@Override
    public void registerInstance(String serviceName, Instance instance) throws NacosException {
        registerInstance(serviceName, Constants.DEFAULT_GROUP, instance);
    }

紧接着调用的registerInstance方法如下,这个方法实现了两个功能:
第一、查看心跳时间配置的对不对(心跳默认为5s)
第二、通过NamingClientProxy 这个代理来执行服务注册操作

#com.alibaba.nacos.client.naming.NacosNamingService;
#num = 142 row
@Override
    public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
        NamingUtils.checkInstanceIsLegal(instance);
        clientProxy.registerService(serviceName, groupName, instance);
    }

通过clientProxy我们发现NamingClientProxy这个代理借口的具体实现是由NamingClientProxyDelegate来实现的,这个可以从NacosNamingService构造方法中来看出。

#com.alibaba.nacos.client.naming.NacosNamingService;
#num = 80 row
public NacosNamingService(Properties properties) throws NacosException {
        init(properties);
    }

初始化方法init中

#com.alibaba.nacos.client.naming.NacosNamingService;
#num = 84 row
private void init(Properties properties) throws NacosException {
        ValidatorUtils.checkInitParam(properties);
        this.namespace = InitUtils.initNamespaceForNaming(properties);
        InitUtils.initSerialization();
        InitUtils.initWebRootContext(properties);
        initLogName(properties);
        
        this.changeNotifier = new InstancesChangeNotifier();
        NotifyCenter.registerToPublisher(InstancesChangeEvent.class, 16384);
        NotifyCenter.registerSubscriber(changeNotifier);
        this.serviceInfoHolder = new ServiceInfoHolder(namespace, properties);
        this.clientProxy = new NamingClientProxyDelegate(this.namespace, serviceInfoHolder, properties, changeNotifier);# 这里我们可以看出来,是NamingClientProxyDelegate来实现的
    }

1.4、 NamingClientProxyDelegate中的实现

根据上方的分析和源码的阅读,我们可以发现NamingClientProxy调用registerService实际调用的就是NamingClientProxyDeletagate的对应方法。

#com.alibaba.nacos.client.naming.remote.NamingClientProxyDelegate;
# num = 92 row
@Override
    public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
        getExecuteClientProxy(instance).registerService(serviceName, groupName, instance);
    }

真正调用注册服务的并不是代理实现类,而是根据当前实例是否为瞬时对象,来选着对应的客户端代理来进行请求的:
如果当前对象是瞬时对象,则采用gRPC协议(NamingGrpcClientProxy)进行请求,否则采用http协议(NamingHttpProxy)进行请求。默认为瞬时对象,也就是说,2.0版本中默认采用了gRPC协议进行与Nacos服务进行交互。

#com.alibaba.nacos.client.naming.remote.NamingClientProxyDelegate;
# num = 175 row
 private NamingClientProxy getExecuteClientProxy(Instance instance) {
        return instance.isEphemeral() ? grpcClientProxy : httpClientProxy;
    }

1.5、NamingGrpcClientProxy中实现

关于gRPC协议(NamingGrpcClientProxy),我们后续展开,我们主要关注一下registerService方法实现,这里其实做了两件事情:
1.缓存当前注册的实例信息用于恢复,缓存的数据结构为ConcurrentMap<String,Instance>,key为“serviceNaming@@groupName”,value就是前面封装的实例信息。
2.另外一件事情就是封装了参数,给予gRPC进行服务的调用和结果的处理。

#com.alibaba.nacos.client.naming.remote.gprc.NamingGrpcClientProxy;
# num = 109 row
 @Override
    public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
        NAMING_LOGGER.info("[REGISTER-SERVICE] {} registering service {} with instance {}", namespaceId, serviceName,
                instance);
        redoService.cacheInstanceForRedo(serviceName, groupName, instance);//缓存数据
        doRegisterService(serviceName, groupName, instance);//基于gRPC进行的服务的调用
    }

通过NamingTest.testServiceList()整个的调用流程如下:
在这里插入图片描述
在这里插入图片描述


  1. Nacos数据(如配置和服务)描述信息,如服务版本、权重、容灾策略、负载均衡策略、鉴权配置、各种自定义标签 (label),从作用范围来看,分为服务级别的元信息、集群的元信息及实例的元信息。摘抄与官网 ↩︎

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Nacos和Dubbo都是阿里巴巴开的项目,Nacos是一款基于云原生架构的动态服务发现、配置管理和服务管理平台,而Dubbo是一款高性能的分布式服务框架。 在使用Nacos和Dubbo进行服务调用时,需要使用Nacos提供的服务发现功能来获取可用的Dubbo服务,然后使用Dubbo提供的RPC框架进行远程调用。 下面是一个使用Nacos和Dubbo进行服务调用的客户实现示例: 1. 添加依赖 在项目的pom.xml文件中添加以下依赖: ```xml <dependency> <groupId>com.alibaba.nacos</groupId> <artifactId>nacos-client</artifactId> <version>${nacos.version}</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>dubbo</artifactId> <version>${dubbo.version}</version> </dependency> ``` 其中,${nacos.version}和${dubbo.version}需要根据您使用的版本进行替换。 2. 配置Nacos客户 在使用Nacos之前,需要先配置Nacos客户。可以在application.properties文件中添加以下配置: ```properties # Nacos配置 spring.cloud.nacos.config.server-addr=localhost:8848 spring.cloud.nacos.config.namespace= spring.cloud.nacos.config.file-extension=properties # Dubbo配置 dubbo.registry.address=nacos://localhost:8848 ``` 其中,spring.cloud.nacos.config.server-addr为Nacos服务器地址,dubbo.registry.address为Dubbo注册中心地址。 3. 获取Dubbo服务 使用Nacos提供的服务发现功能,获取可用的Dubbo服务。可以在代码中添加以下方法: ```java public List<Invoker<?>> getDubboService(String serviceName) throws NacosException { // 创建Nacos服务发现客户 NamingService namingService = NacosFactory.createNamingService(nacosProperties.getConfigServerAddr()); // 获取可用的Dubbo服务 List<Instance> instances = namingService.getAllInstances(serviceName); if (instances == null || instances.isEmpty()) { throw new RuntimeException("No available Dubbo service"); } // 将Dubbo服务转换为Invoker List<Invoker<?>> invokers = new ArrayList<>(); for (Instance instance : instances) { URL url = new URL("dubbo", instance.getIp(), instance.getPort(), serviceName); Invoker<?> invoker = new DubboInvoker<Object>(Object.class, url, new RpcClientWrapper()); invokers.add(invoker); } return invokers; } ``` 其中,serviceName为Dubbo服务名称。 4. 远程调用Dubbo服务 获取到可用的Dubbo服务之后,就可以使用Dubbo提供的RPC框架进行远程调用。可以在代码中添加以下方法: ```java public Object invokeDubboService(List<Invoker<?>> invokers, String methodName, Object... args) throws RpcException { // 创建Dubbo调用上下文 RpcContext rpcContext = RpcContext.getContext(); // 随机选择一个Dubbo服务 Invoker<?> invoker = invokers.get(new Random().nextInt(invokers.size())); // 设置Dubbo调用上下文 rpcContext.setInvoker(invoker); rpcContext.setMethodName(methodName); rpcContext.setArguments(args); // 远程调用Dubbo服务 Result result = invoker.invoke(new RpcInvocation(methodName, new Class<?>[0], args)); if (result.hasException()) { throw result.getException(); } return result.getValue(); } ``` 其中,invokers为获取到的Dubbo服务列表,methodName为Dubbo服务方法名,args为Dubbo服务方法参数。 使用以上方法,就可以在Nacos和Dubbo的帮助下,轻松实现服务调用客户

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值