NacosNamingService 是跟服务相关的核心管理类,提供了服务的上下线、服务实例查询、根据健康状态查询实例列表、根据随机权重算法查询单个健康实例、服务监听器订阅\取消订阅、分页获取服务列表。
该类还在初始化时创建了另外3个分支核心类: 客户端心跳管理类BeatReactor、服务列表管理类HostReactor、服务事件管理类EventDispatcher、NacosServer 通信代理类NamingProxy。
目录
NacosNamingService 流程图
NacosNamingService 功能概览
属性
// 服务所属命名空间
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;
初始化方法
//serverList: 以逗号隔开的服务端地址
public NacosNamingService(String serverList) {
Properties properties = new Properties();
properties.setProperty(PropertyKeyConst.SERVER_ADDR, serverList);
init(properties);
}
//根据配置文件中的属性配置初始化 NacosNamingService 的字段属性
public NacosNamingService(Properties properties) {
init(properties);
}
private void init(Properties properties) {
// 首先从系统属性中获取namespace 属性值,如果没有找到从配置文件找namespace 属性
// 否则默认是public
namespace = InitUtils.initNamespaceForNaming(properties);
//把属性值[spring.cloud.nacos.discovery.server-addr]赋给serverList
initServerAddr(properties);
InitUtils.initWebRootContext();
//从系统属性[com.alibaba.nacos.naming.cache.dir]获取 缓存目录
initCacheDir();
initLogName(properties);
//服务变更事件的监听器管理类
eventDispatcher = new EventDispatcher();
// 远程Ncos服务服务的代理类 所有跟远程Nacos服务接口通讯的统一入口
serverProxy = new NamingProxy(namespace, endpoint, serverList);
serverProxy.setProperties(properties);
//创建管理心跳信息的核心类
beatReactor = new BeatReactor(serverProxy,
initClientBeatThreadCount(properties));
//创建 维护和更新本地服务列表、同远程服务器保持同步的 核心类
hostReactor = new HostReactor(eventDispatcher, serverProxy, cacheDir,
isLoadCacheAtStart(properties), initPollingThreadCount(properties));
}
//执行心跳任务的线程数 默认是当前服务的核数/2
private int initClientBeatThreadCount(Properties properties) {
...
return NumberUtils.toInt(properties.getProperty(
PropertyKeyConst.NAMING_CLIENT_BEAT_THREAD_COUNT),
UtilAndComs.DEFAULT_CLIENT_BEAT_THREAD_COUNT);
}
//执行服务类别更新处理的线程数 默认是当前服务的核数/2
private int initPollingThreadCount(Properties properties) {
...
return NumberUtils.toInt(properties.getProperty(
PropertyKeyConst.NAMING_POLLING_THREAD_COUNT),
UtilAndComs.DEFAULT_POLLING_THREAD_COUNT);
}
//执行心跳任务的线程数 默认是当前服务的核数/2
private int initClientBeatThreadCount(Properties properties) {
if (properties == null) {
return UtilAndComs.DEFAULT_CLIENT_BEAT_THREAD_COUNT;
}
return NumberUtils.toInt(properties.getProperty(
PropertyKeyConst.NAMING_CLIENT_BEAT_THREAD_COUNT),
UtilAndComs.DEFAULT_CLIENT_BEAT_THREAD_COUNT);
}
// 设置 loadCacheAtStart 属性
private boolean isLoadCacheAtStart(Properties properties) {
boolean loadCacheAtStart = false;
if (properties != null &&
StringUtils.isNotEmpty(properties.getProperty(
PropertyKeyConst.NAMING_LOAD_CACHE_AT_START))) {
loadCacheAtStart = BooleanUtils.toBoolean(
properties.getProperty(PropertyKeyConst.NAMING_LOAD_CACHE_AT_START));
}
return loadCacheAtStart;
}
//初始化 endpoint 和 serverList [从配置属性获取]
private void initServerAddr(Properties properties) {
serverList = properties.getProperty(PropertyKeyConst.SERVER_ADDR);
endpoint = InitUtils.initEndpoint(properties);
if (StringUtils.isNotEmpty(endpoint)) {
serverList = "";
}
}
//初始化缓存目录
private void initCacheDir() {
cacheDir = System.getProperty("com.alibaba.nacos.naming.cache.dir");
if (StringUtils.isEmpty(cacheDir)) {
cacheDir = System.getProperty("user.home") + "/nacos/naming/" + namespace;
}
}
initClientBeatThreadCount: 执行心跳任务的线程数,建议配置该属性的值,因为如果不配置的话默认是cpu 逻辑核/2 如果是8C的机器就会创建4个核心线程, 实际上发送心跳的任务是5s一次所以该属性设置为1足够了。
initPollingThreadCount : 服务定时更新的线程数,也建议配置该属性的值,因为如果不配置的话默认是cpu 逻辑核/2 如果是8C的机器就会创建4个核心线程, 如果微服务的服务数不多而且变更不是特别频繁的情况下 这个属性可以设置小一些。个人觉得一般设置成1是够了。因为即便1个线程用完了,线程池还会自动扩容。
那么这2个属性在哪里设置呢?我目前还么有知道,如果有读者找到了,麻烦评论区回复下。
核心方法
....
//服务注册
@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";
}
//筛选健康 可用 权重 > 的实例
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;
}
上面的代码已经筛选掉了上层的重载方法 Balancer.RandomByWeight.selectHost 随机权重 算法逻辑大家可以网上找下,这里不赘述
总结
本章介绍了nacos 客户端 最核心的类 NacosNamingService, 作为该类的分支,接下来的章节会单独介绍BeatReactor、HostReactor、NamingProxy、EventDispatcher