我们从Nacos-Client开始说起,那么说到客户端就涉及到服务注册,我们先了解一下Nacos客户端都会将什么信息传递给服务器,我们直接从Nacos Client项目的NamingTest说起
public class NamingTest {
@Test
public void testServiceList() throws Exception {
//nacos注册信息
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, "127.0.0.1:8848");
properties.put(PropertyKeyConst.USERNAME, "nacos");
properties.put(PropertyKeyConst.PASSWORD, "nacos");
//nacos的实例
Instance instance = new Instance();
instance.setIp("1.1.1.1");
instance.setPort(800);
instance.setWeight(2);
//nacos元数据信息,netType表示网络,external表示外网
Map<String, String> map = new HashMap<String, String>();
map.put("netType", "external");
map.put("version", "2.0");//表示版本
instance.setMetadata(map);
//该接口是注册nacos的关键
//工厂模式创建
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 serviceList = namingService.getServicesOfServer(1, 10, expressionSelector);
}
}
它模仿了一个真实的服务注册进Nacos的过程,包括NacosServer连接、实例的创建、实例属性的赋值、注册实例,所以在这个其中包含了服务注册的核心代码,仅从此处的代码分析,可以看出,Nacos注册服务实例时,包含了两大类信息:Nacos Server连接信息和实例信息。
Instance类包含了实例的基础信息之外,还包含了用于存储元数据的metadata(描述数据的数据),类型为HashMap.
在Instance类中还定义了一些默认信息,这些信息通过get方法提供:
public long getInstanceHeartBeatInterval() {
return getMetaDataByKeyWithDefault(PreservedMetadataKeys.HEART_BEAT_INTERVAL,
Constants.DEFAULT_HEART_BEAT_INTERVAL);
}
public long getInstanceHeartBeatTimeOut() {
return getMetaDataByKeyWithDefault(PreservedMetadataKeys.HEART_BEAT_TIMEOUT,
Constants.DEFAULT_HEART_BEAT_TIMEOUT);
}
public long getIpDeleteTimeout() {
return getMetaDataByKeyWithDefault(PreservedMetadataKeys.IP_DELETE_TIMEOUT,
Constants.DEFAULT_IP_DELETE_TIMEOUT);
}
public String getInstanceIdGenerator() {
return getMetaDataByKeyWithDefault(PreservedMetadataKeys.INSTANCE_ID_GENERATOR,
Constants.DEFAULT_INSTANCE_ID_GENERATOR);
}
上面的get方法在需要元数据默认值时会被用到:
这些都是Nacos默认提供的值,也就是当前实例注册时会告诉Nacos Server说:我的心跳间隙、心跳超时等对应的值是多少,你按照这个值来判断我这个实例是否健康。
有了这些信息,我们基本是已经知道注册实例时需要传递什么参数,需要配置什么参数了.
3. NamingService接口:
NamingService接口是Nacos命名服务对外提供的一个统一接口,看对应的源码就可以发现,它提供了大量实例相关的接口方法:
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.nacos.api.naming;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.listener.EventListener;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.alibaba.nacos.api.naming.pojo.ListView;
import com.alibaba.nacos.api.naming.pojo.ServiceInfo;
import com.alibaba.nacos.api.selector.AbstractSelector;
import java.util.List;
/**
* Naming Service.
*
* @author nkorange
*/
public interface NamingService {
/**
* register a instance to service.
*
* @param serviceName name of service
* @param ip instance ip
* @param port instance port
* @throws NacosException nacos exception
*/
//服务实例注册
void registerInstance(String serviceName, String ip, int port) throws NacosException;
/**
* register a instance to service.
*
* @param serviceName name of service
* @param groupName group of service
* @param ip instance ip
* @param port instance port
* @throws NacosException nacos exception
*/
void registerInstance(String serviceName, String groupName, String ip, int port) throws NacosException;
/**
* register a instance to service with specified cluster name.
*
* @param serviceName name of service
* @param ip instance ip
* @param port instance port
* @param clusterName instance cluster name
* @throws NacosException nacos exception
*/
void registerInstance(String serviceName, String ip, int port, String clusterName) throws NacosException;
/**
* register a instance to service with specified cluster name.
*
* @param serviceName name of service
* @param groupName group of service
* @param ip instance ip
* @param port instance port
* @param clusterName instance cluster name
* @throws NacosException nacos exception
*/
void registerInstance(String serviceName, String groupName, String ip, int port, String clusterName)
throws NacosException;
/**
* register a instance to service with specified instance properties.
*
* @param serviceName name of service
* @param instance instance to register
* @throws NacosException nacos exception
*/
void registerInstance(String serviceName, Instance instance) throws NacosException;
/**
* register a instance to service with specified instance properties.
*
* @param serviceName name of service
* @param groupName group of service
* @param instance instance to register
* @throws NacosException nacos exception
*/
void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException;
/**
* batch register instance to service with specified instance properties.
*
* @param serviceName name of service
* @param groupName group of service
* @param instances instances to register
* @throws NacosException nacos exception
* @since 2.1.1
*/
void batchRegisterInstance(String serviceName, String groupName, List<Instance> instances) throws NacosException;
/**
* batch deRegister instance to service with specified instance properties.
*
* @param serviceName name of service
* @param groupName group of service
* @param instances instances to deRegister
* @throws NacosException nacos exception
* @since 2.2.0
*/
void batchDeregisterInstance(String serviceName, String groupName, List<Instance> instances) throws NacosException;
/**
* deregister instance from a service.
*
* @param serviceName name of service
* @param ip instance ip
* @param port instance port
* @throws NacosException nacos exception
*/
//服务实例注销
void deregisterInstance(String serviceName, String ip, int port) throws NacosException;
/**
* deregister instance from a service.
*
* @param serviceName name of service
* @param groupName group of service
* @param ip instance ip
* @param port instance port
* @throws NacosException nacos exception
*/
void deregisterInstance(String serviceName, String groupName, String ip, int port) throws NacosException;
/**
* deregister instance with specified cluster name from a service.
*
* @param serviceName name of service
* @param ip instance ip
* @param port instance port
* @param clusterName instance cluster name
* @throws NacosException nacos exception
*/
void deregisterInstance(String serviceName, String ip, int port, String clusterName) throws NacosException;
/**
* deregister instance with specified cluster name from a service.
*
* @param serviceName name of service
* @param groupName group of service
* @param ip instance ip
* @param port instance port
* @param clusterName instance cluster name
* @throws NacosException nacos exception
*/
void deregisterInstance(String serviceName, String groupName, String ip, int port, String clusterName)
throws NacosException;
/**
* deregister instance with full instance information and default groupName.
*
* @param serviceName name of service
* @param instance instance
* @throws NacosException nacos exception
*/
void deregisterInstance(String serviceName, Instance instance) throws NacosException;
/**
* deregister instance with full instance information.
*
* @param serviceName name of service
* @param groupName group of service
* @param instance instance information
* @throws NacosException nacos exception
*/
void deregisterInstance(String serviceName, String groupName, Instance instance) throws NacosException;
/**
* get all instances of a service.
*
* @param serviceName name of service
* @return A list of instance
* @throws NacosException nacos exception
*/
//获取服务实例列表
List<Instance> getAllInstances(String serviceName) throws NacosException;
/**
* get all instances of a service.
*
* @param serviceName name of service
* @param groupName group of service
* @return A list of instance
* @throws NacosException nacos exception
*/
List<Instance> getAllInstances(String serviceName, String groupName) throws NacosException;
/**
* Get all instances of a service.
*
* @param serviceName name of service
* @param subscribe if subscribe the service
* @return A list of instance
* @throws NacosException nacos exception
*/
List<Instance> getAllInstances(String serviceName, boolean subscribe) throws NacosException;
/**
* Get all instances of a service.
*
* @param serviceName name of service
* @param groupName group of service
* @param subscribe if subscribe the service
* @return A list of instance
* @throws NacosException nacos exception
*/
List<Instance> getAllInstances(String serviceName, String groupName, boolean subscribe) throws NacosException;
/**
* Get all instances within specified clusters of a service.
*
* @param serviceName name of service
* @param clusters list of cluster
* @return A list of qualified instance
* @throws NacosException nacos exception
*/
List<Instance> getAllInstances(String serviceName, List<String> clusters) throws NacosException;
/**
* Get all instances within specified clusters of a service.
*
* @param serviceName name of service
* @param groupName group of service
* @param clusters list of cluster
* @return A list of qualified instance
* @throws NacosException nacos exception
*/
List<Instance> getAllInstances(String serviceName, String groupName, List<String> clusters) throws NacosException;
/**
* Get all instances within specified clusters of a service.
*
* @param serviceName name of service
* @param clusters list of cluster
* @param subscribe if subscribe the service
* @return A list of qualified instance
* @throws NacosException nacos exception
*/
List<Instance> getAllInstances(String serviceName, List<String> clusters, boolean subscribe) throws NacosException;
/**
* Get all instances within specified clusters of a service.
*
* @param serviceName name of service
* @param groupName group of service
* @param clusters list of cluster
* @param subscribe if subscribe the service
* @return A list of qualified instance
* @throws NacosException nacos exception
*/
List<Instance> getAllInstances(String serviceName, String groupName, List<String> clusters, boolean subscribe)
throws NacosException;
/**
* Get qualified instances of service.
*
* @param serviceName name of service.
* @param healthy a flag to indicate returning healthy or unhealthy instances
* @return A qualified list of instance
* @throws NacosException nacos exception
*/
//查询健康服务实例
List<Instance> selectInstances(String serviceName, boolean healthy) throws NacosException;
/**
* Get qualified instances of service.
*
* @param serviceName name of service
* @param groupName group of service
* @param healthy a flag to indicate returning healthy or unhealthy instances
* @return A qualified list of instance
* @throws NacosException nacos exception
*/
List<Instance> selectInstances(String serviceName, String groupName, boolean healthy) throws NacosException;
/**
* Get qualified instances of service.
*
* @param serviceName name of service
* @param healthy a flag to indicate returning healthy or unhealthy instances
* @param subscribe if subscribe the service
* @return A qualified list of instance
* @throws NacosException nacos exception
*/
List<Instance> selectInstances(String serviceName, boolean healthy, boolean subscribe) throws NacosException;
/**
* Get qualified instances of service.
*
* @param serviceName name of service
* @param groupName group of service
* @param healthy a flag to indicate returning healthy or unhealthy instances
* @param subscribe if subscribe the service
* @return A qualified list of instance
* @throws NacosException nacos exception
*/
List<Instance> selectInstances(String serviceName, String groupName, boolean healthy, boolean subscribe)
throws NacosException;
/**
* Get qualified instances within specified clusters of service.
*
* @param serviceName name of service
* @param clusters list of cluster
* @param healthy a flag to indicate returning healthy or unhealthy instances
* @return A qualified list of instance
* @throws NacosException nacos exception
*/
//查询集群中健康的服务实例
List<Instance> selectInstances(String serviceName, List<String> clusters, boolean healthy) throws NacosException;
/**
* Get qualified instances within specified clusters of service.
*
* @param serviceName name of service
* @param groupName group of service
* @param clusters list of cluster
* @param healthy a flag to indicate returning healthy or unhealthy instances
* @return A qualified list of instance
* @throws NacosException nacos exception
*/
List<Instance> selectInstances(String serviceName, String groupName, List<String> clusters, boolean healthy)
throws NacosException;
/**
* Get qualified instances within specified clusters of service.
*
* @param serviceName name of service
* @param clusters list of cluster
* @param healthy a flag to indicate returning healthy or unhealthy instances
* @param subscribe if subscribe the service
* @return A qualified list of instance
* @throws NacosException nacos exception
*/
List<Instance> selectInstances(String serviceName, List<String> clusters, boolean healthy, boolean subscribe)
throws NacosException;
/**
* Get qualified instances within specified clusters of service.
*
* @param serviceName name of service
* @param groupName group of service
* @param clusters list of cluster
* @param healthy a flag to indicate returning healthy or unhealthy instances
* @param subscribe if subscribe the service
* @return A qualified list of instance
* @throws NacosException nacos exception
*/
List<Instance> selectInstances(String serviceName, String groupName, List<String> clusters, boolean healthy,
boolean subscribe) throws NacosException;
/**
* Select one healthy instance of service using predefined load balance strategy.
*
* @param serviceName name of service
* @return qualified instance
* @throws NacosException nacos exception
*/
//使用负载均衡策略选择一个健康的服务实例
Instance selectOneHealthyInstance(String serviceName) throws NacosException;
/**
* Select one healthy instance of service using predefined load balance strategy.
*
* @param serviceName name of service
* @param groupName group of service
* @return qualified instance
* @throws NacosException nacos exception
*/
Instance selectOneHealthyInstance(String serviceName, String groupName) throws NacosException;
/**
* select one healthy instance of service using predefined load balance strategy.
*
* @param serviceName name of service
* @param subscribe if subscribe the service
* @return qualified instance
* @throws NacosException nacos exception
*/
Instance selectOneHealthyInstance(String serviceName, boolean subscribe) throws NacosException;
/**
* select one healthy instance of service using predefined load balance strategy.
*
* @param serviceName name of service
* @param groupName group of service
* @param subscribe if subscribe the service
* @return qualified instance
* @throws NacosException nacos exception
*/
Instance selectOneHealthyInstance(String serviceName, String groupName, boolean subscribe) throws NacosException;
/**
* Select one healthy instance of service using predefined load balance strategy.
*
* @param serviceName name of service
* @param clusters a list of clusters should the instance belongs to
* @return qualified instance
* @throws NacosException nacos exception
*/
Instance selectOneHealthyInstance(String serviceName, List<String> clusters) throws NacosException;
/**
* Select one healthy instance of service using predefined load balance strategy.
*
* @param serviceName name of service
* @param groupName group of service
* @param clusters a list of clusters should the instance belongs to
* @return qualified instance
* @throws NacosException nacos exception
*/
Instance selectOneHealthyInstance(String serviceName, String groupName, List<String> clusters)
throws NacosException;
/**
* Select one healthy instance of service using predefined load balance strategy.
*
* @param serviceName name of service
* @param clusters a list of clusters should the instance belongs to
* @param subscribe if subscribe the service
* @return qualified instance
* @throws NacosException nacos exception
*/
Instance selectOneHealthyInstance(String serviceName, List<String> clusters, boolean subscribe)
throws NacosException;
/**
* Select one healthy instance of service using predefined load balance strategy.
*
* @param serviceName name of service
* @param groupName group of service
* @param clusters a list of clusters should the instance belongs to
* @param subscribe if subscribe the service
* @return qualified instance
* @throws NacosException nacos exception
*/
Instance selectOneHealthyInstance(String serviceName, String groupName, List<String> clusters, boolean subscribe)
throws NacosException;
/**
* Subscribe service to receive events of instances alteration.
*
* @param serviceName name of service
* @param listener event listener
* @throws NacosException nacos exception
*/
//订阅服务事件
void subscribe(String serviceName, EventListener listener) throws NacosException;
/**
* Subscribe service to receive events of instances alteration.
*
* @param serviceName name of service
* @param groupName group of service
* @param listener event listener
* @throws NacosException nacos exception
*/
void subscribe(String serviceName, String groupName, EventListener listener) throws NacosException;
/**
* Subscribe service to receive events of instances alteration.
*
* @param serviceName name of service
* @param clusters list of cluster
* @param listener event listener
* @throws NacosException nacos exception
*/
void subscribe(String serviceName, List<String> clusters, EventListener listener) throws NacosException;
/**
* Subscribe service to receive events of instances alteration.
*
* @param serviceName name of service
* @param groupName group of service
* @param clusters list of cluster
* @param listener event listener
* @throws NacosException nacos exception
*/
void subscribe(String serviceName, String groupName, List<String> clusters, EventListener listener)
throws NacosException;
/**
* Unsubscribe event listener of service.
*
* @param serviceName name of service
* @param listener event listener
* @throws NacosException nacos exception
*/
//取消订阅服务事件
void unsubscribe(String serviceName, EventListener listener) throws NacosException;
/**
* unsubscribe event listener of service.
*
* @param serviceName name of service
* @param groupName group of service
* @param listener event listener
* @throws NacosException nacos exception
*/
void unsubscribe(String serviceName, String groupName, EventListener listener) throws NacosException;
/**
* Unsubscribe event listener of service.
*
* @param serviceName name of service
* @param clusters list of cluster
* @param listener event listener
* @throws NacosException nacos exception
*/
void unsubscribe(String serviceName, List<String> clusters, EventListener listener) throws NacosException;
/**
* Unsubscribe event listener of service.
*
* @param serviceName name of service
* @param groupName group of service
* @param clusters list of cluster
* @param listener event listener
* @throws NacosException nacos exception
*/
void unsubscribe(String serviceName, String groupName, List<String> clusters, EventListener listener)
throws NacosException;
/**
* Get all service names from server.
*
* @param pageNo page index
* @param pageSize page size
* @return list of service names
* @throws NacosException nacos exception
*/
//获取所有(或指定)服务名称
ListView<String> getServicesOfServer(int pageNo, int pageSize) throws NacosException;
/**
* Get all service names from server.
*
* @param pageNo page index
* @param pageSize page size
* @param groupName group name
* @return list of service names
* @throws NacosException nacos exception
*/
ListView<String> getServicesOfServer(int pageNo, int pageSize, String groupName) throws NacosException;
/**
* Get all service names from server with selector.
*
* @param pageNo page index
* @param pageSize page size
* @param selector selector to filter the resource
* @return list of service names
* @throws NacosException nacos exception
* @since 0.7.0
*/
ListView<String> getServicesOfServer(int pageNo, int pageSize, AbstractSelector selector) throws NacosException;
/**
* Get all service names from server with selector.
*
* @param pageNo page index
* @param pageSize page size
* @param groupName group name
* @param selector selector to filter the resource
* @return list of service names
* @throws NacosException nacos exception
*/
ListView<String> getServicesOfServer(int pageNo, int pageSize, String groupName, AbstractSelector selector)
throws NacosException;
/**
* Get all subscribed services of current client.
*
* @return subscribed services
* @throws NacosException nacos exception
*/
//获取所有订阅的服务
List<ServiceInfo> getSubscribeServices() throws NacosException;
/**
* get server health status.
*
* @return is server healthy
*/
//获取Nacos服务的状态
String getServerStatus();
/**
* Shutdown the resource service.
*
* @throws NacosException exception.
*/
//主动关闭服务
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”。
@Override
public void registerInstance(String serviceName, Instance instance) throws NacosException {
registerInstance(serviceName, Constants.DEFAULT_GROUP, instance);
}
紧接着调用的registerInstance方法如下,这个方法实现了两个功能:
第一,检查心跳时间设置的对不对(心跳默认为5秒)
第二,通过NamingClientProxy这个代理来执行服务注册操作
@Override
public void registerInstance(String serviceName, String groupName, Instance instance) throws NacosException {
NamingUtils.checkInstanceIsLegal(instance);//检查心跳
clientProxy.registerService(serviceName, groupName, instance);//通过代理执行服务注册操作
}
通过clientProxy我们发现NamingClientProxy这个代理接口的具体实现是有NamingClientProxyDelegate来完成的,这个可以从NacosNamingService构造方法中来看出。
public NacosNamingService(Properties properties) throws NacosException {
init(properties);
}
初始化在init方法中:
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来完成的
}
NamingClientProxyDelegate中实现:
根据上方的分析和源码的阅读,我们可以发现NamingClientProxy调用registerService实际上调用的就是NamingClientProxyDelegate的对应方法:
@Override
public void registerService(String serviceName, String groupName, Instance instance) throws NacosException {
getExecuteClientProxy(instance).registerService(serviceName, groupName, instance);
}
真正调用注册服务的并不是代理实现类,而是根据当前实例是否为瞬时对象,来选择对应的客户端代理来进行请求的:
如果当前实例为瞬时对象,则采用gRPC协议(NamingGrpcClientProxy)进行请求,否则采用http协议(NamingHttpClientProxy)进行请求。默认为瞬时对象,也就是说,2.0版本中默认采用了gRPC协议进行与Nacos服务进行交互。
private NamingClientProxy getExecuteClientProxy(Instance instance) {
return instance.isEphemeral() ? grpcClientProxy : httpClientProxy;
}
NamingGrpcClientProxy中实现
关于gRPC协议(NamingGrpcClientProxy),我们后续在做展开,我们主要关注一下registerService方法实现,这里其实做了两件事情
@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进行服务的调用
}