查询实例列表

  • API
    • 查询实例列表
      • 描述
      • 请求类型
      • 请求路径
      • 请求参数
      • 错误编码
      • 示例请求
      • 示例返回
  • 关键流程源码解析
    • InstanceController
  • 总结

API

查询实例列表

描述

查询服务下的实例列表

请求类型

GET

请求路径

/nacos/v1/ns/instance/list

请求参数

名称类型是否必选描述
serviceName字符串服务名
groupName字符串分组名
namespaceId字符串命名空间ID
clusters字符串,多个集群用逗号分隔集群名称
healthyOnlyboolean否,默认为false是否只返回健康实例

错误编码

错误代码描述语义
400Bad Request客户端请求中的语法错误
403Forbidden没有权限
404Not Found无法找到资源
500Internal Server Error服务器内部错误
200OK正常

示例请求

curl -X GET '127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=nacos.test.1'

示例返回

{"dom": "nacos.test.1","cacheMillis": 1000,"useSpecifiedURL": false,"hosts": [{"valid": true,"marked": false,"instanceId": "10.10.10.10-8888-DEFAULT-nacos.test.1","port": 8888,"ip": "10.10.10.10","weight": 1.0,"metadata": {}}],"checksum": "3bbcf6dd1175203a8afdade0e77a27cd1528787794594","lastRefTime": 1528787794594,"env": "","clusters": ""}

关键流程源码解析

InstanceController

    /**     * Get all instance of input service.     * 获取服务的实例列表     * @param request http request     * @return list of instance     * @throws Exception any error during list     */    @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);    }    /**     * Get service full information with instances.     * 获取服务信息及所有的节点信息     * @param namespaceId namespace id     * @param serviceName service name     * @param agent       agent infor string     * @param clusters    cluster names     * @param clientIP    client ip     * @param udpPort     push udp port     * @param env         env     * @param isCheck     is check request     * @param app         app name     * @param tid         tenant     * @param healthyOnly whether only for healthy check     * @return service full information with instances     * @throws Exception any error during handle     */    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();        Service service = serviceManager.getService(namespaceId, serviceName);        long cacheMillis = switchDomain.getDefaultCacheMillis();        // 如果客户端开启了UDP将客户端加入该服务的UDP推送列表中        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;//获取集群内的所有实例,若不指定集群则获取所有集群的所有实例        srvedIPs = service.srvIPs(Arrays.asList(StringUtils.split(clusters, ",")));        // 服务路由类型过滤IP        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;    }

总结

查询实例列表接口主要需要注意3个方面:
1.当客户端开启了UDP服务,可以将客户端的UDP端口等信息通过该接口注册到服务变更推送队列中。
2.若指定了服务的路由类型,会根据标签过滤服务。(需要自己实现CMDB服务,自定义标签与IP的对应关系)
3.若Nacos服务端中该服务的健康实例数/总实例数 <= 保护阈值 会直接返回所有实例,否则会按照健康/非健康的顺序返回实例列表。