通过Nacos Service提供的服务发现接口:
curl -X GET "http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=sca-provider"
{
"name":"DEFAULT_GROUP@@sca-provider",
"groupName":"DEFAULT_GROUP",
"clusters":"",
"cacheMillis":10000,
"hosts":[
{
"instanceId":"10.244.0.18#18080#DEFAULT#DEFAULT_GROUP@@sca-provider",
"ip":"10.244.0.18",
"port":18080,
"weight":1,
"healthy":true,
"enabled":true,
"ephemeral":true,
"clusterName":"DEFAULT",
"serviceName":"DEFAULT_GROUP@@sca-provider",
"metadata":{
"dubbo.metadata-service.urls":"[ "dubbo://10.244.0.18:20880/com.alibaba.cloud.dubbo.service.DubbometadataService?anyhost=true&application=sca-provider&bind.ip=10.244.0.18&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&group=sca-provider&interface=com.alibaba.cloud.dubbo.service.DubbometadataService&methods=getAllServiceKeys,getServiceRestmetadata,getExportedURLs,getAllExportedURLs&pid=8548&qos.enable=false&release=2.7.6&revision=2.2.1.RELEASE&side=provider×tamp=1642390851245&version=1.0.0" ]",
"preserved.register.source":"SPRING_CLOUD",
"dubbo.protocols.dubbo.port":"20880"
},
"ipDeleteTimeout":30000,
"instanceHeartBeatTimeOut":15000,
"instanceHeartBeatInterval":5000
}
],
"lastRefTime":1642391370735,
"checksum":"",
"allIPs":false,
"reachProtectionThreshold":false,
"valid":true
}
先说整体思路:
1.根据namespaceId、serviceName获得Service实例
2.从Service实例中基于srvIPs得到所有服务提供者的实例信息
3.遍历组装JSON字符串返回
一、接口 com.alibaba.nacos.naming.controllers.InstanceController#list
@GetMapping("/list")
@Secured(parser = NamingResourceParser.class, action = ActionTypes.READ)
public Object list(HttpServletRequest request) throws Exception {
String namespaceId = WebUtils.optional(request, CommonParams.NAMESPACE_ID, Constants.DEFAULT_NAMESPACE_ID);
String serviceName = WebUtils.required(request, CommonParams.SERVICE_NAME);
NamingUtils.checkServiceNameFormat(serviceName);
.........................................
// 调用服务com.alibaba.nacos.naming.core.InstanceOperatorServiceImpl#listInstance
return getInstanceOperator().listInstance(namespaceId, serviceName, subscriber, clusters, healthyOnly);
}
二、获取实例信息
根据namespaceId和serviceName获取双层Map中的Service服务实例
再把Service中的实例列表转换成集合直接返回
@Override
public ServiceInfo listInstance(String namespaceId, String serviceName, Subscriber subscriber, String cluster,
boolean healthOnly) throws Exception {
ServiceInfo result = new ServiceInfo(serviceName, cluster);
// 根据namespaceId和serviceName(spring.application.name)从双层map中获取服务实例
Service service = serviceManager.getService(namespaceId, serviceName);
long cacheMillis = switchDomain.getDefaultCacheMillis();
...............................
// 获取服务对应的实例列表
List srvedIps = service
.srvIPs(Arrays.asList(StringUtils.split(cluster, StringUtils.COMMA)));
..............
// 把实例信息按健康状态分组放到ipMap
long total = 0;
Map> ipMap = new HashMap<>(2);
ipMap.put(Boolean.TRUE, new ArrayList<>());
ipMap.put(Boolean.FALSE, new ArrayList<>());
for (com.alibaba.nacos.naming.core.Instance ip : srvedIps) {
// remove disabled instance:
if (!ip.isEnabled()) {
continue;
}
ipMap.get(ip.isHealthy()).add(ip);
total += 1;
}
double threshold = service.getProtectThreshold();
// 将ipMap中的实例扔到hosts集合中,有两种情况
// 1.健康的实例数量低于阈值,则直接按所有实例设置healthy=true
// 2.否则直接扔到hosts集合中
// setReachProtectionThreshold 是否到达保护阈值,会根据两种情况设置
List hosts;
if ((float) ipMap.get(Boolean.TRUE).size() / total <= threshold) {
Loggers.SRV_LOG.warn("protect threshold reached, return all ips, service: {}", result.getName());
result.setReachProtectionThreshold(true);
hosts = Stream.of(Boolean.TRUE, Boolean.FALSE).map(ipMap::get).flatMap(Collection::stream)
.map(InstanceUtil::deepCopy)
// set all to `healthy` state to protect
.peek(instance -> instance.setHealthy(true)).collect(Collectors.toCollection(linkedList::new));
} else {
result.setReachProtectionThreshold(false);
hosts = new linkedList<>(ipMap.get(Boolean.TRUE));
if (!healthOnly) {
hosts.addAll(ipMap.get(Boolean.FALSE));
}
}
result.setHosts(hosts);
result.setCacheMillis(cacheMillis);
result.setLastRefTime(System.currentTimeMillis());
result.setChecksum(service.getChecksum());
return result;
}
三、通过Service获取实例列表的核心方法 com.alibaba.nacos.naming.core.Service#srvIPs
获取集群clusterMap中包含临时和永久的所有实例
public ListsrvIPs(List clusters) { if (CollectionUtils.isEmpty(clusters)) { clusters = new ArrayList<>(); clusters.addAll(clusterMap.keySet()); } return allIPs(clusters); } public List allIPs(List clusters) { List result = new ArrayList<>(); for (String cluster : clusters) { Cluster clusterObj = clusterMap.get(cluster); if (clusterObj == null) { continue; } result.addAll(clusterObj.allIPs()); } return result; }
四、从Cluster获取临时和永久的实例
com.alibaba.nacos.naming.core.Cluster#allIPs()
public List allIPs() {
List allInstances = new ArrayList<>();
allInstances.addAll(persistentInstances);
allInstances.addAll(ephemeralInstances);
return allInstances;
}



