本章主题
1、客户端如何获取服务
2、服务端服务发现接口
3、客户端订阅
1、客户端如何获取服务
public List<Instance> getAllInstances(String serviceName) throws NacosException {
return this.getAllInstances(serviceName, (List)(new ArrayList()));
}
public List<Instance> getAllInstances(String serviceName, String groupName) throws NacosException {
return this.getAllInstances(serviceName, groupName, new ArrayList());
}
public List<Instance> getAllInstances(String serviceName, boolean subscribe) throws NacosException {
return this.getAllInstances(serviceName, (List)(new ArrayList()), subscribe);
}
public List<Instance> getAllInstances(String serviceName, String groupName, boolean subscribe) throws NacosException {
return this.getAllInstances(serviceName, groupName, new ArrayList(), subscribe);
}
public List<Instance> getAllInstances(String serviceName, List<String> clusters) throws NacosException {
return this.getAllInstances(serviceName, clusters, true);
}
public List<Instance> getAllInstances(String serviceName, String groupName, List<String> clusters) throws NacosException {
return this.getAllInstances(serviceName, groupName, clusters, true);
}
public List<Instance> getAllInstances(String serviceName, List<String> clusters, boolean subscribe) throws NacosException {
return this.getAllInstances(serviceName, "DEFAULT_GROUP", clusters, subscribe);
}
public List<Instance> getAllInstances(String serviceName, String groupName, List<String> clusters, boolean subscribe) throws NacosException {
ServiceInfo serviceInfo;
if (subscribe) {
// subscribe == true
// 这里是先从本地缓存中获取,获取不到再从远程获取
serviceInfo = this.hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ","));
} else {
serviceInfo = this.hostReactor.getServiceInfoDirectlyFromServer(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ","));
}
List list;
// 这里就是把ServiceInfo的host转换返回出去
return (List)(serviceInfo != null && !CollectionUtils.isEmpty(list = serviceInfo.getHosts()) ? list : new ArrayList());
}
�
这里我们看一下ServiceInfo数据结构
@JsonInclude(Include.NON_NULL)
public class ServiceInfo {
@JsonIgnore
private String jsonFromServer = "";
public static final String SPLITER = "@@";
private String name;
private String groupName;
private String clusters;
private long cacheMillis = 1000L;
// 保存实例集合
private List<Instance> hosts = new ArrayList();
private long lastRefTime = 0L;
private String checksum = "";
private volatile boolean allIPs = false;
private static final String EMPTY = "";
private static final String ALL_IPS = "000--00-ALL_IPS--00--000";
�
我们在看一下怎么做操作的
public ServiceInfo getServiceInfo(String serviceName, String clusters) {
LogUtils.NAMING_LOGGER.debug("failover-mode: " + this.failoverReactor.isFailoverSwitch());
String key = ServiceInfo.getKey(serviceName, clusters);
if (this.failoverReactor.isFailoverSwitch()) {
return this.failoverReactor.getService(key);
} else {
// 从缓存中获取
ServiceInfo serviceObj = this.getServiceInfo0(serviceName, clusters);
if (null == serviceObj) {
serviceObj = new ServiceInfo(serviceName, clusters);
this.serviceInfoMap.put(serviceObj.getKey(), serviceObj);
this.updatingMap.put(serviceName, new Object());
// 远程获取
this.updateServiceNow(serviceName, clusters);
this.updatingMap.remove(serviceName);
} else if (this.updatingMap.containsKey(serviceName)) {
synchronized(serviceObj) {
try {
serviceObj.wait(5000L);
} catch (InterruptedException var8) {
LogUtils.NAMING_LOGGER.error("[getServiceInfo] serviceName:" + serviceName + ", clusters:" + clusters, var8);
}
}
}
// 开启定时获取服务任务
this.scheduleUpdateIfAbsent(serviceName, clusters);
return (ServiceInfo)this.serviceInfoMap.get(serviceObj.getKey());
}
}
这里流程如下
1、this.getServiceInfo0()从缓存中获取
2、获取不到就从远程获取
3、开启定时获取服务任务
1.1、缓存获取
private ServiceInfo getServiceInfo0(String serviceName, String clusters) {
String key = ServiceInfo.getKey(serviceName, clusters);
return (ServiceInfo)this.serviceInfoMap.get(key);
}
这里的serviceInfoMap就是本地缓存
1.2、远程获取
private void updateServiceNow(String serviceName, String clusters) {
try {
this.updateService(serviceName, clusters);
} catch (NacosException var4) {
LogUtils.NAMING_LOGGER.error("[NA] failed to update serviceName: " + serviceName, var4);
}
}
public void updateService(String serviceName, String clusters) throws NacosException {
// 远程访问/instance/list接口
// 这里带了udp端口
String result = this.serverProxy.queryList(serviceName, clusters, this.pushReceiver.getUdpPort(), false);
// 解析返回值
this.processServiceJson(result);
}
这里需要注意,这里是带udp端口的
1.3、解析返回值
public ServiceInfo processServiceJson(String json) {
ServiceInfo serviceInfo = (ServiceInfo)JacksonUtils.toObj(json, ServiceInfo.class);
ServiceInfo oldService = (ServiceInfo)this.serviceInfoMap.get(serviceInfo.getKey());
if (this.pushEmptyProtection && !serviceInfo.validate()) {
return oldService;
} else {
boolean changed = false;
if (oldService != null) {
if (oldService.getLastRefTime() > serviceInfo.getLastRefTime()) {
LogUtils.NAMING_LOGGER.warn("out of date data received, old-t: " + oldService.getLastRefTime() + ", new-t: " + serviceInfo.getLastRefTime());
}
// 如果已经存在该服务,那么就直接用最新的覆盖
this.serviceInfoMap.put(serviceInfo.getKey(), serviceInfo);
Map<String, Instance> oldHostMap = new HashMap(oldService.getHosts().size());
Iterator var6 = oldService.getHosts().iterator();
while(var6.hasNext()) {
Instance host = (Instance)var6.next();
oldHostMap.put(host.toInetAddr(), host);
}
Map<String, Instance> newHostMap = new HashMap(serviceInfo.getHosts().size());
Iterator var16 = serviceInfo.getHosts().iterator();
while(var16.hasNext()) {
Instance host = (Instance)var16.next();
newHostMap.put(host.toInetAddr(), host);
}
Set<Instance> modHosts = new HashSet();
Set<Instance> newHosts = new HashSet();
Set<Instance> remvHosts = new HashSet();
List<Entry<String, Instance>> newServiceHosts = new ArrayList(newHostMap.entrySet());
Iterator var11 = newServiceHosts.iterator();
while(true) {
Entry entry;
Instance host;
String key;
while(var11.hasNext()) {
entry = (Entry)var11.next();
host = (Instance)entry.getValue();
key = (String)entry.getKey();
if (oldHostMap.containsKey(key) && !StringUtils.equals(host.toString(), ((Instance)oldHostMap.get(key)).toString())) {
modHosts.add(host);
} else if (!oldHostMap.containsKey(key)) {
newHosts.add(host);
}
}
var11 = oldHostMap.entrySet().iterator();
while(var11.hasNext()) {
entry = (Entry)var11.next();
host = (Instance)entry.getValue();
key = (String)entry.getKey();
if (!newHostMap.containsKey(key) && !newHostMap.containsKey(key)) {
remvHosts.add(host);
}
}
if (newHosts.size() > 0) {
changed = true;
LogUtils.NAMING_LOGGER.info("new ips(" + newHosts.size() + ") service: " + serviceInfo.getKey() + " -> " + JacksonUtils.toJson(newHosts));
}
if (remvHosts.size() > 0) {
changed = true;
LogUtils.NAMING_LOGGER.info("removed ips(" + remvHosts.size() + ") service: " + serviceInfo.getKey() + " -> " + JacksonUtils.toJson(remvHosts));
}
if (modHosts.size() > 0) {
changed = true;
this.updateBeatInfo(modHosts);
LogUtils.NAMING_LOGGER.info("modified ips(" + modHosts.size() + ") service: " + serviceInfo.getKey() + " -> " + JacksonUtils.toJson(modHosts));
}
serviceInfo.setJsonFromServer(json);
if (newHosts.size() > 0 || remvHosts.size() > 0 || modHosts.size() > 0) {
NotifyCenter.publishEvent(new InstancesChangeEvent(serviceInfo.getName(), serviceInfo.getGroupName(), serviceInfo.getClusters(), serviceInfo.getHosts()));
DiskCache.write(serviceInfo, this.cacheDir);
}
break;
}
} else {
changed = true;
LogUtils.NAMING_LOGGER.info("init new ips(" + serviceInfo.ipCount() + ") service: " + serviceInfo.getKey() + " -> " + JacksonUtils.toJson(serviceInfo.getHosts()));
// 如果不存在就是直接放缓存
this.serviceInfoMap.put(serviceInfo.getKey(), serviceInfo);
NotifyCenter.publishEvent(new InstancesChangeEvent(serviceInfo.getName(), serviceInfo.getGroupName(), serviceInfo.getClusters(), serviceInfo.getHosts()));
serviceInfo.setJsonFromServer(json);
DiskCache.write(serviceInfo, this.cacheDir);
}
MetricsMonitor.getServiceInfoMapSizeMonitor().set((double)this.serviceInfoMap.size());
if (changed) {
LogUtils.NAMING_LOGGER.info("current ips:(" + serviceInfo.ipCount() + ") service: " + serviceInfo.getKey() + " -> " + JacksonUtils.toJson(serviceInfo.getHosts()));
}
return serviceInfo;
}
}
这里看起来解析返回值代码多,实际上都是做别的操作,具体代码只有俩行
1、(ServiceInfo)JacksonUtils.toObj(json, ServiceInfo.class) 解析json
2、this.serviceInfoMap.put(serviceInfo.getKey(), serviceInfo); 不管当前缓存是否存在都直接存
1.4、开启定时任务获取服务端最新数据
�
public void scheduleUpdateIfAbsent(String serviceName, String clusters) {
if (this.futureMap.get(ServiceInfo.getKey(serviceName, clusters)) == null) {
synchronized(this.futureMap) {
if (this.futureMap.get(ServiceInfo.getKey(serviceName, clusters)) == null) {
ScheduledFuture<?> future = this.addTask(new HostReactor.UpdateTask(serviceName, clusters));
this.futureMap.put(ServiceInfo.getKey(serviceName, clusters), future);
}
}
}
}
public synchronized ScheduledFuture<?> addTask(HostReactor.UpdateTask task) {
return this.executor.schedule(task, 1000L, TimeUnit.MILLISECONDS);
}
�这里我们看一下具体任务
public void run() {
long delayTime = 1000L;
try {
ServiceInfo serviceObj = (ServiceInfo)HostReactor.this.serviceInfoMap.get(ServiceInfo.getKey(this.serviceName, this.clusters));
if (serviceObj == null) {
HostReactor.this.updateService(this.serviceName, this.clusters);
return;
}
if (serviceObj.getLastRefTime() <= this.lastRefTime) {
HostReactor.this.updateService(this.serviceName, this.clusters);
serviceObj = (ServiceInfo)HostReactor.this.serviceInfoMap.get(ServiceInfo.getKey(this.serviceName, this.clusters));
} else {
HostReactor.this.refreshOnly(this.serviceName, this.clusters);
}
this.lastRefTime = serviceObj.getLastRefTime();
if (!HostReactor.this.notifier.isSubscribed(this.serviceName, this.clusters) && !HostReactor.this.futureMap.containsKey(ServiceInfo.getKey(this.serviceName, this.clusters))) {
LogUtils.NAMING_LOGGER.info("update task is stopped, service:" + this.serviceName + ", clusters:" + this.clusters);
return;
}
if (CollectionUtils.isEmpty(serviceObj.getHosts())) {
this.incFailCount();
return;
}
delayTime = serviceObj.getCacheMillis();
this.resetFailCount();
} catch (Throwable var7) {
this.incFailCount();
LogUtils.NAMING_LOGGER.warn("[NA] failed to update serviceName: " + this.serviceName, var7);
} finally {
HostReactor.this.executor.schedule(this, Math.min(delayTime << this.failCount, 60000L), TimeUnit.MILLISECONDS);
}
}
�
2、服务端服务发现接口处理
@GetMapping("/list")
@Secured(parser = NamingResourceParser.class, action = ActionTypes.READ)
public ObjectNode 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);
String agent = WebUtils.getUserAgent(request);
String clusters = WebUtils.optional(request, "clusters", StringUtils.EMPTY);
String clientIP = WebUtils.optional(request, "clientIP", StringUtils.EMPTY);
int udpPort = Integer.parseInt(WebUtils.optional(request, "udpPort", "0"));
String env = WebUtils.optional(request, "env", StringUtils.EMPTY);
boolean isCheck = Boolean.parseBoolean(WebUtils.optional(request, "isCheck", "false"));
String app = WebUtils.optional(request, "app", StringUtils.EMPTY);
String tenant = WebUtils.optional(request, "tid", StringUtils.EMPTY);
boolean healthyOnly = Boolean.parseBoolean(WebUtils.optional(request, "healthyOnly", "false"));
// 就这里核心代码
return doSrvIpxt(namespaceId, serviceName, agent, clusters, clientIP, udpPort, env, isCheck, app, tenant,
healthyOnly);
}
�
public ObjectNode doSrvIpxt(String namespaceId, String serviceName, String agent, String clusters, String clientIP,
int udpPort, String env, boolean isCheck, String app, String tid, boolean healthyOnly) throws Exception {
ClientInfo clientInfo = new ClientInfo(agent);
ObjectNode result = JacksonUtils.createEmptyJsonNode();
// 根据 namespaceId 和serviceName找Service
Service service = serviceManager.getService(namespaceId, serviceName);
long cacheMillis = switchDomain.getDefaultCacheMillis();
// now try to enable the push
try {
if (udpPort > 0 && pushService.canEnablePush(agent)) {
// 保存订阅
pushService
.addClient(namespaceId, serviceName, clusters, agent, new InetSocketAddress(clientIP, udpPort),
pushDataSource, tid, app);
cacheMillis = switchDomain.getPushCacheMillis(serviceName);
}
} catch (Exception e) {
Loggers.SRV_LOG
.error("[NACOS-API] failed to added push client {}, {}:{}", clientInfo, clientIP, udpPort, e);
cacheMillis = switchDomain.getDefaultCacheMillis();
}
if (service == null) {
if (Loggers.SRV_LOG.isDebugEnabled()) {
Loggers.SRV_LOG.debug("no instance to serve for service: {}", serviceName);
}
result.put("name", serviceName);
result.put("clusters", clusters);
result.put("cacheMillis", cacheMillis);
result.replace("hosts", JacksonUtils.createEmptyArrayNode());
return result;
}
checkIfDisabled(service);
List<Instance> srvedIPs;
// 这里就是拿取Service里的 clusterMap,也就是所有实例列表
srvedIPs = service.srvIPs(Arrays.asList(StringUtils.split(clusters, ",")));
// filter ips using selector:
if (service.getSelector() != null && StringUtils.isNotBlank(clientIP)) {
srvedIPs = service.getSelector().select(clientIP, srvedIPs);
}
if (CollectionUtils.isEmpty(srvedIPs)) {
if (Loggers.SRV_LOG.isDebugEnabled()) {
Loggers.SRV_LOG.debug("no instance to serve for service: {}", serviceName);
}
if (clientInfo.type == ClientInfo.ClientType.JAVA
&& clientInfo.version.compareTo(VersionUtil.parseVersion("1.0.0")) >= 0) {
result.put("dom", serviceName);
} else {
result.put("dom", NamingUtils.getServiceName(serviceName));
}
result.put("name", serviceName);
result.put("cacheMillis", cacheMillis);
result.put("lastRefTime", System.currentTimeMillis());
result.put("checksum", service.getChecksum());
result.put("useSpecifiedURL", false);
result.put("clusters", clusters);
result.put("env", env);
result.set("hosts", JacksonUtils.createEmptyArrayNode());
result.set("metadata", JacksonUtils.transferToJsonNode(service.getMetadata()));
return result;
}
Map<Boolean, List<Instance>> ipMap = new HashMap<>(2);
ipMap.put(Boolean.TRUE, new ArrayList<>());
ipMap.put(Boolean.FALSE, new ArrayList<>());
for (Instance ip : srvedIPs) {
ipMap.get(ip.isHealthy()).add(ip);
}
if (isCheck) {
result.put("reachProtectThreshold", false);
}
double threshold = service.getProtectThreshold();
if ((float) ipMap.get(Boolean.TRUE).size() / srvedIPs.size() <= threshold) {
Loggers.SRV_LOG.warn("protect threshold reached, return all ips, service: {}", serviceName);
if (isCheck) {
result.put("reachProtectThreshold", true);
}
ipMap.get(Boolean.TRUE).addAll(ipMap.get(Boolean.FALSE));
ipMap.get(Boolean.FALSE).clear();
}
if (isCheck) {
result.put("protectThreshold", service.getProtectThreshold());
result.put("reachLocalSiteCallThreshold", false);
return JacksonUtils.createEmptyJsonNode();
}
ArrayNode hosts = JacksonUtils.createEmptyArrayNode();
// 这里加入返回参数
for (Map.Entry<Boolean, List<Instance>> entry : ipMap.entrySet()) {
List<Instance> ips = entry.getValue();
if (healthyOnly && !entry.getKey()) {
continue;
}
for (Instance instance : ips) {
// remove disabled instance:
if (!instance.isEnabled()) {
continue;
}
ObjectNode ipObj = JacksonUtils.createEmptyJsonNode();
ipObj.put("ip", instance.getIp());
ipObj.put("port", instance.getPort());
// deprecated since nacos 1.0.0:
ipObj.put("valid", entry.getKey());
ipObj.put("healthy", entry.getKey());
ipObj.put("marked", instance.isMarked());
ipObj.put("instanceId", instance.getInstanceId());
ipObj.set("metadata", JacksonUtils.transferToJsonNode(instance.getMetadata()));
ipObj.put("enabled", instance.isEnabled());
ipObj.put("weight", instance.getWeight());
ipObj.put("clusterName", instance.getClusterName());
if (clientInfo.type == ClientInfo.ClientType.JAVA
&& clientInfo.version.compareTo(VersionUtil.parseVersion("1.0.0")) >= 0) {
ipObj.put("serviceName", instance.getServiceName());
} else {
ipObj.put("serviceName", NamingUtils.getServiceName(instance.getServiceName()));
}
ipObj.put("ephemeral", instance.isEphemeral());
hosts.add(ipObj);
}
}
// 这里加入关键参数 也就是实例
result.replace("hosts", hosts);
if (clientInfo.type == ClientInfo.ClientType.JAVA
&& clientInfo.version.compareTo(VersionUtil.parseVersion("1.0.0")) >= 0) {
result.put("dom", serviceName);
} else {
result.put("dom", NamingUtils.getServiceName(serviceName));
}
result.put("name", serviceName);
result.put("cacheMillis", cacheMillis);
result.put("lastRefTime", System.currentTimeMillis());
result.put("checksum", service.getChecksum());
result.put("useSpecifiedURL", false);
result.put("clusters", clusters);
result.put("env", env);
result.replace("metadata", JacksonUtils.transferToJsonNode(service.getMetadata()));
return result;
}
这里代码量特别多,实际上就干了三件事
1、Service service = serviceManager.getService(namespaceId, serviceName);�
根据 namespaceId 和serviceName找Service�2、pushService .addClient(namespaceId, serviceName, clusters, agent, new InetSocketAddress(clientIP, udpPort), pushDataSource, tid, app);
保存订阅3、srvedIPs = service.srvIPs(Arrays.asList(StringUtils.split(clusters, “,”)));�
拿取Service里的 clusterMap,也就是所有实例列表�然后返回这个实例列表
这里我们直接看订阅
2.1、保存订阅
public void addClient(String namespaceId, String serviceName, String clusters, String agent,
InetSocketAddress socketAddr, DataSource dataSource, String tenant, String app) {
// 把客户端信息封装成一个PushClient 尤其是udp端口,这样在添加和删除服务的时候就可以根据这个订阅找到客户端给客户端发送信息
PushClient client = new PushClient(namespaceId, serviceName, clusters, agent, socketAddr, dataSource, tenant,
app);
// 加入到clientMap ,在注册服务的时候 会有一个udp调用能看到
addClient(client);
}
public void addClient(PushClient client) {
// client is stored by key 'serviceName' because notify event is driven by serviceName change
String serviceKey = UtilsAndCommons.assembleFullServiceName(client.getNamespaceId(), client.getServiceName());
ConcurrentMap<String, PushClient> clients = clientMap.get(serviceKey);
if (clients == null) {
clientMap.putIfAbsent(serviceKey, new ConcurrentHashMap<>(1024));
clients = clientMap.get(serviceKey);
}
PushClient oldClient = clients.get(client.toString());
if (oldClient != null) {
oldClient.refresh();
} else {
PushClient res = clients.putIfAbsent(client.toString(), client);
if (res != null) {
Loggers.PUSH.warn("client: {} already associated with key {}", res.getAddrStr(), res.toString());
}
Loggers.PUSH.debug("client: {} added for serviceName: {}", client.getAddrStr(), client.getServiceName());
}
}
这里就是把客户端信息 + 客户端的udp端口封装成PushClient,然后保存到clientMap里
:::info
这里我们看一下服务注册的时候的怎么利用订阅模式推送的
:::
public void onApplicationEvent(ServiceChangeEvent event) {
Service service = event.getService();
String serviceName = service.getName();
String namespaceId = service.getNamespaceId();
Future future = GlobalExecutor.scheduleUdpSender(() -> {
try {
Loggers.PUSH.info(serviceName + " is changed, add it to push queue.");
// 获取所有的订阅者
ConcurrentMap<String, PushClient> clients = clientMap
.get(UtilsAndCommons.assembleFullServiceName(namespaceId, serviceName));
if (MapUtils.isEmpty(clients)) {
return;
}
Map<String, Object> cache = new HashMap<>(16);
long lastRefTime = System.nanoTime();
// 遍历订阅者
for (PushClient client : clients.values()) {
if (client.zombie()) {
Loggers.PUSH.debug("client is zombie: " + client.toString());
clients.remove(client.toString());
Loggers.PUSH.debug("client is zombie: " + client.toString());
continue;
}
Receiver.AckEntry ackEntry;
Loggers.PUSH.debug("push serviceName: {} to client: {}", serviceName, client.toString());
String key = getPushCacheKey(serviceName, client.getIp(), client.getAgent());
byte[] compressData = null;
Map<String, Object> data = null;
if (switchDomain.getDefaultPushCacheMillis() >= 20000 && cache.containsKey(key)) {
org.javatuples.Pair pair = (org.javatuples.Pair) cache.get(key);
compressData = (byte[]) (pair.getValue0());
data = (Map<String, Object>) pair.getValue1();
Loggers.PUSH.debug("[PUSH-CACHE] cache hit: {}:{}", serviceName, client.getAddrStr());
}
if (compressData != null) {
// 把Client封装成AckEntry
ackEntry = prepareAckEntry(client, compressData, data, lastRefTime);
} else {
ackEntry = prepareAckEntry(client, prepareHostsData(client), lastRefTime);
if (ackEntry != null) {
cache.put(key, new org.javatuples.Pair<>(ackEntry.origin.getData(), ackEntry.data));
}
}
Loggers.PUSH.info("serviceName: {} changed, schedule push for: {}, agent: {}, key: {}",
client.getServiceName(), client.getAddrStr(), client.getAgent(),
(ackEntry == null ? null : ackEntry.key));
// udp 发送的地方
udpPush(ackEntry);
}
} catch (Exception e) {
Loggers.PUSH.error("[NACOS-PUSH] failed to push serviceName: {} to client, error: {}", serviceName, e);
} finally {
futureMap.remove(UtilsAndCommons.assembleFullServiceName(namespaceId, serviceName));
}
}, 1000, TimeUnit.MILLISECONDS);
futureMap.put(UtilsAndCommons.assembleFullServiceName(namespaceId, serviceName), future);
}
这里我们会发现利用的事件通知,然后遍历clients,这个就是clientMap,最后进行udpPush()进行udp发送
2.2、获取所有实例列表,并返回
public List<Instance> srvIPs(List<String> clusters) {
if (CollectionUtils.isEmpty(clusters)) {
clusters = new ArrayList<>();
clusters.addAll(clusterMap.keySet());
}
return allIPs(clusters);
}
public List<Instance> allIPs(List<String> clusters) {
List<Instance> result = new ArrayList<>();
for (String cluster : clusters) {
Cluster clusterObj = clusterMap.get(cluster);
if (clusterObj == null) {
continue;,
}
result.addAll(clusterObj.allIPs());
}
return result;
}
�这里其实并有什么难度,就是这里执行的方法首先是Service里的,Service里保存的Map,把这些Map统统转化成InstanceList,然后返回出去