当前位置: 首页 > 科技观察

Nacos客户端服务注册源码解析

时间:2023-03-16 17:50:46 科技观察

开篇思路在这之前写了十多篇Nacos文章,感觉Nacos值得多学习。于是一篇写Nacos源码系列专栏的文章诞生了。写作的目标有两个:一是能够系统的学习Nacos的知识;第二,能够基于Nacos学习所涉及的知识点或方面;呈现形式也有两种:第一,单篇文章足够简单,而且有价值;第二,发现代码中的新奇之处;源码版本信息目前在生产实践中推荐使用1.4.2版本,但作为技术研究,本系列文章仅基于2.0.2版本进行讲解。这是跨度比较大的两个版本。建议大家结合源码学习。关于源码拉取,环境搭建部分不再赘述。下面开始这篇文章来讲解Nacos服务注册的client部分。服务注册信息说到服务注册,我们首先要了解Nacos会向服务器传递哪些信息。直接从NacosClient项目的NamingTest看:(PropertyKeyConst.PASSWORD,"nacos");Instanceinstance=newInstance();instance.setIp("1.1.1.1");instance.setPort(800);instance.setWeight(2);Mapma??p=newHashMap();map.put("netType","external");map.put("version","2.0");instance.setMetadata(map);NamingServicenamingService=NacosFactory.createNamingService(properties);namingService.registerInstance("nacos.test.1",instance);这是服务注册的核心代码。从这里的代码分析可以看出,Nacos在注册一个服务实例时,包含两类信息:NacosServer连接信息和实例信息。NacosServer连接信息NacosServer连接信息存储在Properties中,包括以下信息:Serveraddress:Nacos服务器地址,属性key为serverAddr;Username:连接Nacos服务的用户名,属性key为username,默认值为nacos;Password:连接Nacos服务的密码,属性键为password,默认值为nacos;实例信息注册实例信息由Instance对象携带,注册的实例信息分为两部分:实例基本信息和元数据。实例的基本信息包括:instanceId:实例的唯一ID;ip:实例IP,提供给消费者进行通信的地址;port:端口,提供给消费者访问的端口;weight:权重,当前实例的权限,浮点型(默认1.0D);healthy:健康状态,默认true;enabled:实例是否准备好接收请求,默认true;ephemeral:实例是否为transient,默认为true;clusterName:实例所属集群的名称;serviceName:实例服务信息;Instance类不仅包含实例的基本信息,还包含用于存储元数据的元数据(描述数据的数据),类型为HashMap。放两个demo中的数据:netType:顾名思义,网络类型,这里的值为external,表示外网;version:version,Nacos的版本,这里是2.0的大版本。除了Demo中的“自定义”信息外,Instance类中还定义了一些默认信息,通过get方法提供:PreservedMetadataKeys.HEART_BEAT_TIMEOUT,Constants.DEFAULT_HEART_BEAT_TIMEOUT);}publiclonggetIpDeleteTimeout(){returngetMetaDataByKeyWithDefault(PreservedMetadataKeys.IP_DELETE_TIMEOUT,Constants.DEFAULT_IP_DELETE_TIMEOUT);}publicStringgetInstanceIdGenerator(){returngetMetaDataByKeyWithDefault(PreservedMetadataKeys.INSTANCE_ID_GENERATOR,Constants.DEFAULT_INSTANCE_ID_GENERATOR);}上面的get方法在需要元The将使用数据的默认值:preserved.heart.beat.interval:心跳间隔的关键,默认为5s,即默认每5秒执行一次心跳;preserved.heart.beat.timeout:心跳超时的关键,默认为15s,即默认15秒没有收到心跳,实例将被标记为不健康;preserved.ip.delete.timeout:要删除的实例IP的key,默认30s,即30秒没有收到心跳,实例将被移除;preserved.instance.id.generator:实例ID生成器key,默认为si简单;这些是Nacos提供的默认值,也就是当前实例注册的时候,会告诉NacosServer:我的心跳间隔,心跳超时等对应的值是多少,你可以判断是否根据这个值,我的实例是健康的。当然,如果你想让心跳“加速”,快速排除故障,可以缩短心跳间隔和超时时间。但这也意味着对Nacos服务施加一些压力。有了这些信息,我们基本上就已经知道注册实例需要传递哪些参数,需要配置哪些参数了。NamingService接口NamingService接口是Nacos命名服务提供的统一接口。你可以通过查看相应的源代码找到它。它提供了大量与实例相关的接口方法,例如:服务实例注册;服务实例取消;获取服务实例列表;获取服务单实例;订阅服务事件;取消订阅服务事件;获取所有(或指定的)服务名称;获取所有订阅的服务;获取Nacos服务的状态;针对不同场景、不同类型的实例或服务进行筛选。这个就不一一解释了,大家根据自己的需要或者意见来使用即可。NamingService是通过NamingFactory类和上面提到的Nacos服务信息来实例化的:构造函数constructor=driverImplClass.getConstructor(Properties.class);return(NamingService)constructor.newInstance(properties);}catch(Throwablee){thrownewNacosException(NacosException.CLIENT_INVALID_PARAM,e);}}很明显,这里使用了反射机制来实例化NamingService,接口的具体实现类是NacosNamingService班级。NacosNamingService的实现使用示例代码中的NamingService#registerInstance方法注册服务实例。该方法接收两个参数,服务名称和实例对象。@OverridepublicvoidregisterInstance(StringserviceName,Instanceinstance)throwsNacosException{registerInstance(serviceName,Constants.DEFAULT_GROUP,instance);}这个方法最大的作用就是设置当前实例的分组信息。我们知道,在Nacos中,实例是通过Namespace、group、Service、Cluster等层层与环境隔离的,这里设置的默认分组为“DEFAULT_GROUP”。立即调用的registerInstance方法如下:@OverridepublicvoidregisterInstance(StringserviceName,StringgroupName,Instanceinstance)throwsNacosException{NamingUtils.checkInstanceIsLegal(instance);clientProxy.registerService(serviceName,groupName,instance);}这个方法实现了两个功能:首先,检查是心跳时间设置对了吗?配置的超时时间不能小于心跳间隔。二是通过NamingClientProxy代理进行服务注册操作。回顾NacosNamingService的构造方法,会发现代理接口NamingClientProxy的具体实现是由NamingClientProxyDelegate完成的。在NamingClientProxyDelegate中实现NamingClientProxy调用registerService其实调用的是NamingClientProxyDelegate对应的方法:@OverridepublicvoidregisterService(StringserviceName,StringgroupName,Instanceinstance)throwsNacosException{getExecuteClientProxy(instance).registerService(serviceName,groupName,instance);}代理实际上并没有调用注册服务实现类,但根据当前实例是否为瞬态对象,选择对应的客户端代理进行请求:,使用gRPC协议(NamingGrpcClientProxy)进行请求,否则使用http协议(NamingHttpClientProxy)进行请求。默认是transientobject,也就是说在2.0版本中,默认使用gRPC协议与Nacos服务进行交互。NamingGrpcClientProxy中gRPC协议的实现我们会单独讲解,这里暂时不展开。主要看其registerService方法的实现:@OverridepublicvoidregisterService(StringserviceName,StringgroupName,Instanceinstance)throwsNacosException{NAMING_LOGGER.info("[REGISTER-SERVICE]{}registeringservice{}withinstance{}",namespaceId,serviceName,instance);namingGrpcInstanceNameEventListener.cheo,groupName,instance);InstanceRequestrequest=newInstanceRequest(namespaceId,serviceName,groupName,NamingRemoteConstants.REGISTER_INSTANCE,instance);requestToServer(request,Response.class);}NamingGrpcClientProxy中做了两件事,一件事是通过事件监听器当前注册的实例信息被缓存以供恢复。缓存的数据结构是ConcurrentMap。还有就是封装参数,基于gRPC协议调用服务和处理结果。下面看一张流程图,总结一下上面提到的整个业务逻辑:总结这是Nacos源码分析的开始,主要分析服务注册需要哪些维度的信息,以及客户端提供的核心服务处理类(NamingService)和注册通信协议选择。部分内容还可以细化,比如gRPC协议的实现等,我们会在后续的文章中继续呈现。