nacos服务发现
服务发现是客户端发起负载均衡(feign)调用接口的时候内部第一次调用nacos服务端接口的时候去调用的
后续调用基本上都是从客户端的缓存列表里边去取,拿不到才会向服务端发起调用
如果想看这一块代码可以看下ribbion源码分析ribbon源码分析
上一篇:nacos服务注册
服务发现源码分析
NamingService为我们提供服务注册和服务发现功能
NacosNamingService是NamingService的实现类
@Override
public List<Instance> getAllInstances(String serviceName, String groupName, List<String> clusters,
boolean subscribe) throws NacosException {
ServiceInfo serviceInfo;
// 判断是否需要订阅服务信息(默认为 true)
if (subscribe) {
// 订阅服务信息
serviceInfo = hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName),
StringUtils.join(clusters, ","));
} else {
// 直接去nacos拉取服务信息
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;
}
HostReactor
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);
}
//读取本地服务列表的缓存,缓存是一个Map
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());
}
**updateServiceNow(serviceName, clusters);**方法
private void updateServiceNow(String serviceName, String clusters) {
try {
updateService(serviceName, clusters);
} catch (NacosException e) {
NAMING_LOGGER.error("[NA] failed to update serviceName: " + serviceName, e);
}
}
//调用远程服务
public void updateService(String serviceName, String clusters) throws NacosException {
ServiceInfo oldService = getServiceInfo0(serviceName, clusters);
try {
String result = serverProxy.queryList(serviceName, clusters, pushReceiver.getUdpPort(), false);
if (StringUtils.isNotEmpty(result)) {
processServiceJson(result);
}
} finally {
if (oldService != null) {
synchronized (oldService) {
oldService.notifyAll();
}
}
}
}
想服务端发起调用
定时任务同步客户端缓存列表
scheduleUpdateIfAbsent(serviceName, clusters);
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;
}
//启动定时任务
ScheduledFuture<?> future = addTask(new UpdateTask(serviceName, clusters));
futureMap.put(ServiceInfo.getKey(serviceName, clusters), future);
}
}
在UpdateTask中调用run方法,并切在run方法中调用updateService方法
updateService方法上边已经讲过
服务端接收客户端请求服务发现接口
/nacos/v1/ns/instance/list
这个就不看了,比较简单
服务端可以参考文档并搜索 拉取服务列表接口:拉取服务列表接口
总结:
Nacos的服务发现分为两种模式:
模式一:主动拉取模式,消费者定期主动从Nacos拉取服务列表并缓存起来,再服务调用时优先读取本地缓存中的服务列表。
模式二:订阅模式,消费者订阅Nacos中的服务列表,并基于UDP协议来接收服务变更通知。当Nacos中的服务列表更新时,会发送UDP广播给所有订阅者。这一种是在服务端界面操作或者主动调用服务注册接口或者下线接口等都会主动推送给客户端需要更改的注册列表
与Eureka相比,Nacos的订阅模式服务状态更新更及时,消费者更容易及时发现服务列表的变化,剔除故障服务