目录
学习目标:作为客户端的服务中枢类,需要知道它提供了哪些核心功能,跟它上下游的类是如何交互的。
简介:
NacosNamingService 是跟服务相关的核心管理类,提供了服务的上下线、服务实例查询、根据健康状态查询实例列表、根据随机权重算法查询单个健康实例、服务监听器订阅\取消订阅、分页查询服务列表等众多能力。
该类还在初始化时创建了另外3个分支核心类: 客户端心跳管理类BeatReactor、本地服务列表管理类HostReactor、服务事件管理类EventDispatcher、NacosServer 通信代理类NamingProxy。
NacosNamingService 流程图
NacosNamingService 功能概览
核心属性
public class NacosNamingService implements NamingService {
// 服务所属命名空间
private String namespace;
// 提供一个服务地址 可以动态拉取serverList
// 区别于静态配置的serverList属性
private String endpoint;
//服务列表以逗号隔开
private String serverList;
//服务列表的缓存和failover的目录
private String cacheDir;
//日志名称
private String logName;
//维护本地的服务列表管理
private HostReactor hostReactor;
//负责心跳任务管理
private BeatReactor beatReactor;
//负责服务变更事件的监听器管理
private EventDispatcher eventDispatcher;
//负责跟nacos server 的接口通信
private NamingProxy serverProxy;
}
初始化方法
初始化方法创建了4个非常重要的对象
//维护本地的服务列表管理 private HostReactor hostReactor;
//负责心跳任务管理 private BeatReactor beatReactor;
//负责跟nacos server 的接口通信 private NamingProxy serverProxy;
//负责服务变更事件的监听器管理 private EventDispatcher eventDispatcher;
initClientBeatThreadCount:执行心跳任务的线程数,建议配置该属性的值,因为如果不配置的话默认是cpu 逻辑核/2 如果是8C的机器就会创建4个核心线程, 实际上发送心跳的任务是5s一次所以该属性设置为1足够了。
initPollingThreadCount :服务定时更新的线程数,也建议配置该属性的值,因为如果不配置的话默认是cpu 逻辑核/2 如果是8C的机器就会创建4个核心线程, 如果微服务的服务数不多而且变更不是特别频繁的情况下 这个属性可以设置小一些。个人觉得一般设置成1是够了。因为即便1个线程用完了,线程池还会自动扩容。
核心功能
....
//服务注册
@Override
public void registerInstance(String serviceName, String groupName, Instance
instance) throws NacosException {
if (instance.isEphemeral()) {
//创建心跳对象
BeatInfo beatInfo = new BeatInfo();
beatInfo.setServiceName(NamingUtils.getGroupedName(serviceName, groupName));
beatInfo.setIp(instance.getIp());
beatInfo.setPort(instance.getPort());
beatInfo.setCluster(instance.getClusterName());
beatInfo.setWeight(instance.getWeight());
beatInfo.setMetadata(instance.getMetadata());
beatInfo.setScheduled(false);
// 默认5秒一次向服务端发送心跳报告
long instanceInterval = instance.getInstanceHeartBeatInterval();
beatInfo.setPeriod(instanceInterval == 0 ? DEFAULT_HEART_BEAT_INTERVAL :
instanceInterval);
//把心跳对象交给 beatReactor 定时执行 key 为groupname@@serviceName
beatReactor.addBeatInfo(NamingUtils.getGroupedName(serviceName, groupName),
beatInfo);
}
//serverProxy 完成跟远程服务器的注册请求
serverProxy.registerService(NamingUtils.getGroupedName(serviceName, groupName),
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);
}
//查询服务下的实例列表
@Override
public List<Instance> getAllInstances(String serviceName, String groupName,
List<String> clusters, boolean subscribe) throws NacosException {
ServiceInfo serviceInfo;
// 如果subscribe 为true 从本地获取[本地么有再从远程拉取]
if (subscribe) {
//优先从本地列表获取
serviceInfo =
hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName,
groupName), StringUtils.join(clusters, ","));
} else {
// 如果subscribe 为false 直接从从远程拉取
serviceInfo =
hostReactor.getServiceInfoDirectlyFromServer(
NamingUtils.getGroupedName(serviceName, groupName),
StringUtils.join(clusters, ","));
}
List<Instance> list;
if (serviceInfo == null || CollectionUtils.isEmpty(list =
serviceInfo.getHosts())) {
return new ArrayList<Instance>();
}
return list;
}
//根据 healthy 状态筛选实例
@Override
public List<Instance> selectInstances(String serviceName, String groupName,
List<String> clusters, boolean healthy, boolean subscribe) throws
NacosException {
ServiceInfo serviceInfo;
if (subscribe) {
serviceInfo =
hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName,
groupName), StringUtils.join(clusters, ","));
} else {
serviceInfo =
hostReactor.getServiceInfoDirectlyFromServer(
NamingUtils.getGroupedName(serviceName, groupName),
StringUtils.join(clusters, ","));
}
return selectInstances(serviceInfo, healthy);
}
@Override
public Instance selectOneHealthyInstance(String serviceName, String groupName,
List<String> clusters, boolean subscribe) throws NacosException {
// 根据随机权重算法选择一个健康的节点
if (subscribe) {
return Balancer.RandomByWeight.selectHost(
hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName,
groupName), StringUtils.join(clusters, ",")));
} else {
return Balancer.RandomByWeight.selectHost(
hostReactor.getServiceInfoDirectlyFromServer(
NamingUtils.getGroupedName(serviceName, groupName),
StringUtils.join(clusters, ",")));
}
}
//订阅服务监听器
@Override
public void subscribe(String serviceName, String groupName, List<String> clusters,
EventListener listener) throws NacosException {
eventDispatcher.addListener(
hostReactor.getServiceInfo(
NamingUtils.getGroupedName(serviceName, groupName),
StringUtils.join(clusters, ",")),
StringUtils.join(clusters, ","), listener);
}
//取消服务监听器
@Override
public void unsubscribe(String serviceName, String groupName, List<String> clusters,
EventListener listener) throws NacosException {
eventDispatcher.removeListener(NamingUtils.getGroupedName(serviceName,
groupName), StringUtils.join(clusters, ","), listener);
}
//根据 groupName 和 selector 查询service 名称列表
@Override
public ListView<String> getServicesOfServer(int pageNo, int pageSize, String
groupName, AbstractSelector selector) throws NacosException {
return serverProxy.getServiceList(pageNo, pageSize, groupName, selector);
}
//查询所有被订阅监听器的服务列表
@Override
public List<ServiceInfo> getSubscribeServices() {
return eventDispatcher.getSubscribeServices();
}
//查询服务器的状态
@Override
public String getServerStatus() {
return serverProxy.serverHealthy() ? "UP" : "DOWN";
}
//筛选健康 可用 权重 >0 的实例
private List<Instance> selectInstances(ServiceInfo serviceInfo, boolean healthy) {
List<Instance> list;
if (serviceInfo == null || CollectionUtils.isEmpty(list =
serviceInfo.getHosts())) {
return new ArrayList<Instance>();
}
Iterator<Instance> iterator = list.iterator();
while (iterator.hasNext()) {
Instance instance = iterator.next();
if (healthy != instance.isHealthy() || !instance.isEnabled() ||
instance.getWeight() <= 0) {
iterator.remove();
}
}
return list;
}
从代码NacosNamingService核心功能包括:
服务的上下线、服务实例查询、根据健康状态查询实例列表、根据随机权重算法查询单个健康实例、服务监听器订阅\取消订阅、分页查询服务列表等众多能力
其中Balancer.RandomByWeight.selectHost 随机权重 算法逻辑大家可以先在网上找下,后面我们单独开一篇讲下这个算法
总结
通过NacosNamingService类的解析我们看到作为nacos client 的中枢类地位可谓非常重要。
所有的核心类都是在这里被初始化的。而且提供了完整的服务功能。
作为该类的分支,接下来的章节会单独介绍BeatReactor、HostReactor、NamingProxy、EventDispatcher等类