Nacos源码解析系列目录
- Nacos 源码编译运行
- (Nacos源码解析一)Nacos 注册实例源码解析
- (Nacos源码解析二)Nacos 服务发现源码解析
- (Nacos源码解析三)Nacos 心跳机制源码解析
- (Nacos源码解析四)Nacos服务健康检查源码解析
- (Nacos源码解析五)Nacos服务事件变动源码解析
服务发现源码解析
核心API
Nacos 服务发现源码图
大家可以根据源码图,一步一步看着各节点源码解析
客户端
1、客户端获取查询实例列表
通过服务名称 获取和当前服务名称相关的服务实例
com.alibaba.nacos.client.naming.NacosNamingService#getAllInstances(java.lang.String)
com.alibaba.nacos.client.naming.NacosNamingService#getAllInstances(java.lang.String, java.lang.String, java.util.List<java.lang.String>, boolean)
@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 {
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;
}
获取服务信息
com.alibaba.nacos.client.naming.core.HostReactor#getServiceInfo
public ServiceInfo getServiceInfo(final String serviceName, final String clusters) {
NAMING_LOGGER.debug("failover-mode: " + failoverReactor.isFailoverSwitch());
String key = ServiceInfo.getKey(serviceName, clusters);
if (failoverReactor.isFailoverSwitch()) {
return failoverReactor.getService(key);
}
// 获取ServiceInfo
ServiceInfo serviceObj = getServiceInfo0(serviceName, clusters);
// 第一次获取是空的
if (null == serviceObj) {
serviceObj = new ServiceInfo(serviceName, clusters);
serviceInfoMap.put(serviceObj.getKey(), serviceObj);
updatingMap.put(serviceName, new Object());
// 更新服务列表
updateServiceNow(serviceName, clusters);
updatingMap.remove(serviceName);
} else if (updatingMap.containsKey(serviceName)) {
if (UPDATE_HOLD_INTERVAL > 0) {
// hold a moment waiting for update finish
synchronized (serviceObj) {
try {
serviceObj.wait(UPDATE_HOLD_INTERVAL);
} catch (InterruptedException e) {
NAMING_LOGGER
.error("[getServiceInfo] serviceName:" + serviceName + ", clusters:" + clusters, e);
}
}
}
}
// 开启定时任务
scheduleUpdateIfAbsent(serviceName, clusters);
return serviceInfoMap.get(serviceObj.getKey());
}
获取客户端的服务实例缓存信息
com.alibaba.nacos.client.naming.core.HostReactor#getServiceInfo0
private ServiceInfo getServiceInfo0(String serviceName, String clusters) {
String key = ServiceInfo.getKey(serviceName, clusters);
return serviceInfoMap.get(key);
}
获取服务端最新的实例,将获取的实例列表放入serviceInfoMap
com.alibaba.nacos.client.naming.core.HostReactor#updateServiceNow
private void updateServiceNow(String serviceName, String clusters) {
try {
updateService(serviceName, clusters);
} catch (NacosException e) {
NAMING_LOGGER.error("[NA] failed to update serviceName: " + serviceName, e);
}
}
调用Service API 获取 实例数据并存入缓存
com.alibaba.nacos.client.naming.core.HostReactor#updateService
public void updateService(String serviceName, String clusters) throws NacosException {
ServiceInfo oldService = getServiceInfo0(serviceName, clusters);
try {
// 查询实例列表API
String result = serverProxy.queryList(serviceName, clusters, pushReceiver.getUdpPort(), false);
if (StringUtils.isNotEmpty(result)) {
// 将获取的实例列表放入serviceInfoMap
processServiceJson(result);
}
} finally {
if (oldService != null) {
synchronized (oldService) {
oldService.notifyAll();
}
}
}
}
2、定时获取最新实例列表,更新客户端的服务缓存
com.alibaba.nacos.client.naming.core.HostReactor#scheduleUpdateIfAbsent
public void scheduleUpdateIfAbsent(String serviceName, String clusters) {
if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) != null) {
return;
}
synchronized (futureMap) {
if (futureMap.get(ServiceInfo.getKey(serviceName, clusters)) != null) {
return;
}
// 开启schedule任务
ScheduledFuture<?> future = addTask(new UpdateTask(serviceName, clusters));
futureMap.put(ServiceInfo.getKey(serviceName, clusters), future);
}
}
定时获取服务端最新实例数据并更新到本地缓存
com.alibaba.nacos.client.naming.core.HostReactor.UpdateTask#run
@Override
public void run() {
long delayTime = DEFAULT_DELAY;
try {
ServiceInfo serviceObj = serviceInfoMap.get(ServiceInfo.getKey(serviceName, clusters));
if (serviceObj == null) {
// 获取最新的实例列表,并更新serviceInfoMap
updateService(serviceName, clusters);
return;
}
if (serviceObj.getLastRefTime() <= lastRefTime) {
updateService(serviceName, clusters);
serviceObj = serviceInfoMap.get(ServiceInfo.getKey(serviceName, clusters));
} else {
// if serviceName already updated by push, we should not override it
// since the push data may be different from pull through force push
refreshOnly(serviceName, clusters);
}
lastRefTime = serviceObj.getLastRefTime();
if (!notifier.isSubscribed(serviceName, clusters) && !futureMap
.containsKey(ServiceInfo.getKey(serviceName, clusters))) {
// abort the update task
NAMING_LOGGER.info("update task is stopped, service:" + serviceName + ", clusters:" + clusters);
return;
}
if (CollectionUtils.isEmpty(serviceObj.getHosts())) {
incFailCount();
return;
}
delayTime = serviceObj.getCacheMillis();
resetFailCount();
} catch (Throwable e) {
incFailCount();
NAMING_LOGGER.warn("[NA] failed to update serviceName: " + serviceName, e);
} finally {
// 嵌套调度
executor.schedule(this, Math.min(delayTime << failCount, DEFAULT_DELAY * 60), TimeUnit.MILLISECONDS);
}
}
服务端
1、客户端调用服务端获取全部实例接口
com.alibaba.nacos.naming.controllers.InstanceController#list
com.alibaba.nacos.naming.controllers.InstanceController#doSrvIpxt
从serviceMap 获取Service
public ObjectNode doSrvIpxt(String namespaceId, String serviceName, String agent, String cluste
int udpPort, String env, boolean isCheck, String app, String tid, boolean healthyOnly)
ClientInfo clientInfo = new ClientInfo(agent);
ObjectNode result = JacksonUtils.createEmptyJsonNode();
// 获取服务
// Map(namespace, Map(group::serviceName, Service)).
// private final Map<String, Map<String, Service>> serviceMap = new ConcurrentHashMap<>();
// 从Nacos的serviceMap获取
Service service = serviceManager.getService(namespaceId, serviceName);
long cacheMillis = switchDomain.getDefaultCacheMillis();
...
// 获取全部实例
srvedIPs = service.srvIPs(Arrays.asList(StringUtils.split(clusters, ",")));
...
}
com.alibaba.nacos.naming.core.Service#srvIPs
com.alibaba.nacos.naming.core.Service#allIPs(java.util.List<java.lang.String>)
com.alibaba.nacos.naming.core.Cluster#allIPs()
public List<Instance> allIPs() {
List<Instance> allInstances = new ArrayList<>();
// 持久化实例 CP架构
// Set<Instance> persistentInstances = new HashSet<>();
allInstances.addAll(persistentInstances);
// 临时实例 AP架构
// private Set<Instance> ephemeralInstances = new HashSet<>();
allInstances.addAll(ephemeralInstances);
return allInstances;
}