gRPC 和 v3 HTTP 的核心差异对比:
gRPC 路径(正常)
ServiceQueryRequestHandler.handle()
cluster = request.getCluster() == null ? "" : request.getCluster() // 空串
→ ServiceUtil.doSelectInstances(serviceInfo, "", ...)
→ clusterSets = StringUtils.isNotBlank("") ? ... : new HashSet<>() // 空 Set
→ checkCluster(空Set, instance) → clusterSets.isEmpty() → return true // 全部通过
v3 HTTP 路径(有问题)
InstanceListForm.fillDefaultValue()
clusterName 为空 → 填充为 "DEFAULT" // 强制覆盖
→ CatalogServiceV2Impl.listInstances(..., "DEFAULT")
→ serviceStorage.getClusters(service).contains("DEFAULT") // false,cluster-hba ≠ DEFAULT
→ throw NacosException NOT_FOUND → controller 返回空 data
结论: 两套路径用了完全不同的实现。gRPC 走 ServiceUtil.doSelectInstances,空 cluster 时 clusterSets 为空 Set,checkCluster 直接返回 true 放行所有实例。v3 HTTP 走
CatalogServiceV2Impl.listInstances,InstanceListForm.fillDefaultValue() 把空 cluster 强制填成 "DEFAULT",然后做精确匹配,找不到就抛异常。
这是 v3 HTTP API 新增时没有复用 ServiceUtil 的过滤逻辑,导致行为不一致,属于 Bug。修复方式是 InstanceListForm.fillDefaultValue() 不应该对 clusterName
做默认填充,或者 listInstances 里 clusterName 为空时走 listAllInstances 的逻辑。
gRPC 和 v3 HTTP 的核心差异对比:
gRPC 路径(正常)
ServiceQueryRequestHandler.handle()
cluster = request.getCluster() == null ? "" : request.getCluster() // 空串
→ ServiceUtil.doSelectInstances(serviceInfo, "", ...)
→ clusterSets = StringUtils.isNotBlank("") ? ... : new HashSet<>() // 空 Set
→ checkCluster(空Set, instance) → clusterSets.isEmpty() → return true // 全部通过
v3 HTTP 路径(有问题)
InstanceListForm.fillDefaultValue()
clusterName 为空 → 填充为 "DEFAULT" // 强制覆盖
→ CatalogServiceV2Impl.listInstances(..., "DEFAULT")
→ serviceStorage.getClusters(service).contains("DEFAULT") // false,cluster-hba ≠ DEFAULT
→ throw NacosException NOT_FOUND → controller 返回空 data
结论: 两套路径用了完全不同的实现。gRPC 走 ServiceUtil.doSelectInstances,空 cluster 时 clusterSets 为空 Set,checkCluster 直接返回 true 放行所有实例。v3 HTTP 走
CatalogServiceV2Impl.listInstances,InstanceListForm.fillDefaultValue() 把空 cluster 强制填成 "DEFAULT",然后做精确匹配,找不到就抛异常。
这是 v3 HTTP API 新增时没有复用 ServiceUtil 的过滤逻辑,导致行为不一致,属于 Bug。修复方式是 InstanceListForm.fillDefaultValue() 不应该对 clusterName
做默认填充,或者 listInstances 里 clusterName 为空时走 listAllInstances 的逻辑。