diff --git a/README.md b/README.md index 2f6029f90..924422a48 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,11 @@ [中文说明](README_ZH.md) -Dubbo Admin is a console for better visualization of Dubbo services, it provides fully support for Dubbo3 and is compatible with 2.7.x, 2.6.x and 2.5.x. +Dubbo Admin is the console designed for better visualization of Dubbo services, it provides support for Dubbo3 and is compatible with 2.7.x, 2.6.x and 2.5.x. ![index](https://raw.githubusercontent.com/apache/dubbo-admin/develop/doc/images/index.png) -There are four ways to deploy Dubbo Admin to a production environment +There are four ways to deploy Dubbo Admin to a production environment. 1. [Linux with Admin](#11-linux-with-admin) 2. [Docker with Admin](#12-docker-with-admin) diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/common/util/SyncUtils.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/common/util/SyncUtils.java index 34b4e6557..6841aae50 100644 --- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/common/util/SyncUtils.java +++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/common/util/SyncUtils.java @@ -31,6 +31,7 @@ import java.util.Map; import static org.apache.dubbo.admin.common.util.Constants.COLON; +import static org.apache.dubbo.admin.common.util.Constants.INTERFACE_KEY; public class SyncUtils { @@ -201,7 +202,7 @@ public static >> Pair filte } private static String getServiceInterface(URL url) { - String serviceInterface = url.getServiceInterface(); + String serviceInterface = url.getOriginalParameter(INTERFACE_KEY); if (StringUtils.isBlank(serviceInterface) || Constants.ANY_VALUE.equals(serviceInterface)) { serviceInterface = url.getPath(); } diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/config/ConfigCenter.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/config/ConfigCenter.java index 0e51b623a..49377a2fd 100644 --- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/config/ConfigCenter.java +++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/config/ConfigCenter.java @@ -25,15 +25,19 @@ import org.apache.dubbo.admin.registry.mapping.impl.NacosServiceMapping; import org.apache.dubbo.admin.registry.mapping.impl.NoOpServiceMapping; import org.apache.dubbo.admin.registry.metadata.MetaDataCollector; +import org.apache.dubbo.admin.registry.metadata.impl.DubboDelegateMetadataCollector; import org.apache.dubbo.admin.registry.metadata.impl.NoOpMetadataCollector; import org.apache.dubbo.admin.service.impl.InstanceRegistryCache; import org.apache.commons.lang3.StringUtils; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.beans.factory.ScopeBeanFactory; import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.Logger; import org.apache.dubbo.common.logger.LoggerFactory; import org.apache.dubbo.metadata.MappingListener; +import org.apache.dubbo.metadata.report.MetadataReport; +import org.apache.dubbo.metadata.report.MetadataReportInstance; import org.apache.dubbo.registry.Registry; import org.apache.dubbo.registry.RegistryFactory; import org.apache.dubbo.registry.RegistryService; @@ -46,6 +50,7 @@ import org.springframework.context.annotation.DependsOn; import java.util.Arrays; +import java.util.Optional; import static org.apache.dubbo.common.constants.CommonConstants.CLUSTER_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.ENABLE_EMPTY_PROTECTION_KEY; @@ -181,6 +186,15 @@ MetaDataCollector getMetadataCollector() { metaDataCollector.init(); } else { logger.warn("you are using dubbo.registry.address, which is not recommend, please refer to: https://github.com/apache/dubbo-admin/wiki/Dubbo-Admin-configuration"); + ApplicationModel applicationModel = ApplicationModel.defaultModel(); + ScopeBeanFactory beanFactory = applicationModel.getBeanFactory(); + MetadataReportInstance metadataReportInstance = beanFactory.registerBean(MetadataReportInstance.class); + + Optional metadataReport = metadataReportInstance.getMetadataReports(true) + .values().stream().findAny(); + + metaDataCollector = new DubboDelegateMetadataCollector(metadataReport.get()); + metaDataCollector.setUrl(registryUrl); } return metaDataCollector; } diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ApiDocsController.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ApiDocsController.java index 1eddcaba6..4638040bb 100644 --- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ApiDocsController.java +++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ApiDocsController.java @@ -138,14 +138,18 @@ public String callDubboService(CallDubboServiceRequest dubboCfg, @RequestBody Li if (null == paramValues) { paramValues = new Object[0]; } - CompletableFuture future = ApiDocsDubboGenericUtil.invoke(dubboCfg.getRegistryCenterUrl(), dubboCfg.getInterfaceClassName(), - dubboCfg.getMethodName(), dubboCfg.isAsync(), dubboCfg.getVersion(), paramTypes, paramValues, dubboCfg.getGroup()); try { - Object objResult = future.get(); - return JSON.toJSONString(objResult, CLASS_NAME_PRE_FILTER); - } catch (InterruptedException | ExecutionException e) { - LOG.error(e.getMessage(), e); - return "Some exceptions have occurred, please check the log."; + CompletableFuture future = ApiDocsDubboGenericUtil.invoke(dubboCfg.getRegistryCenterUrl(), dubboCfg.getInterfaceClassName(), + dubboCfg.getMethodName(), dubboCfg.isAsync(), dubboCfg.getVersion(), paramTypes, paramValues, dubboCfg.getGroup()); + try { + Object objResult = future.get(); + return JSON.toJSONString(objResult, CLASS_NAME_PRE_FILTER); + } catch (InterruptedException | ExecutionException e) { + LOG.error(e.getMessage(), e); + return "Some exceptions have occurred, please check the log."; + } + } catch (Exception e) { + return "Error '" + e.getMessage() + "' happened when loading docs from remote. Please make sure api-docs dependency is correctly added to Dubbo providers, check the following link for more instructions: https://cn.dubbo.apache.org/zh-cn/overview/reference/admin/"; } } diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/MetricsCollectController.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/MetricsCollectController.java index aa493f83d..e774e7597 100644 --- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/MetricsCollectController.java +++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/MetricsCollectController.java @@ -31,6 +31,7 @@ import org.apache.dubbo.admin.service.MetricsService; import org.apache.dubbo.admin.service.ProviderService; import org.apache.dubbo.admin.service.impl.MetrcisCollectServiceImpl; +import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.definition.model.FullServiceDefinition; import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier; import org.springframework.beans.factory.annotation.Autowired; @@ -102,35 +103,39 @@ public List searchService(@RequestParam String ip, @RequestParam Stri protected void addMetricsConfigToMap(Map configMap, String ip) { List providers = providerService.findByAddress(ip); - if (providers.size() > 0) { + if (!providers.isEmpty()) { for (int i = 0; i < providers.size() && configMap.isEmpty(); i++) { Provider provider = providers.get(i); String service = provider.getService(); MetadataIdentifier providerIdentifier = new MetadataIdentifier(Tool.getInterface(service), Tool.getVersion(service), Tool.getGroup(service), Constants.PROVIDER_SIDE, provider.getApplication()); String metaData = providerService.getProviderMetaData(providerIdentifier); - FullServiceDefinition providerServiceDefinition = new Gson().fromJson(metaData, FullServiceDefinition.class); - Map parameters = providerServiceDefinition.getParameters(); - String metricsPort = parameters.get(Constants.METRICS_PORT); - String metricsProtocol = parameters.get(Constants.METRICS_PROTOCOL); - if (metricsPort != null && metricsProtocol != null) { - configMap.put(metricsPort, metricsProtocol); + if (StringUtils.isNotEmpty(metaData)) { + FullServiceDefinition providerServiceDefinition = new Gson().fromJson(metaData, FullServiceDefinition.class); + Map parameters = providerServiceDefinition.getParameters(); + String metricsPort = parameters.get(Constants.METRICS_PORT); + String metricsProtocol = parameters.get(Constants.METRICS_PROTOCOL); + if (metricsPort != null && metricsProtocol != null) { + configMap.put(metricsPort, metricsProtocol); + } } } } else { List consumers = consumerService.findByAddress(ip); - if (consumers.size() > 0) { + if (!consumers.isEmpty()) { for (int i = 0; i < consumers.size() && configMap.isEmpty(); i++) { Consumer consumer = consumers.get(i); String service = consumer.getService(); MetadataIdentifier consumerIdentifier = new MetadataIdentifier(Tool.getInterface(service), Tool.getVersion(service), Tool.getGroup(service), Constants.CONSUMER_SIDE, consumer.getApplication()); String metaData = consumerService.getConsumerMetadata(consumerIdentifier); - Map consumerParameters = new Gson().fromJson(metaData, Map.class); - String metricsPort = consumerParameters.get(Constants.METRICS_PORT); - String metricsProtocol = consumerParameters.get(Constants.METRICS_PROTOCOL); - if (metricsPort != null && metricsProtocol != null) { - configMap.put(metricsPort, metricsProtocol); + if (StringUtils.isNotEmpty(metaData)) { + Map consumerParameters = new Gson().fromJson(metaData, Map.class); + String metricsPort = consumerParameters.get(Constants.METRICS_PORT); + String metricsProtocol = consumerParameters.get(Constants.METRICS_PROTOCOL); + if (metricsPort != null && metricsProtocol != null) { + configMap.put(metricsPort, metricsProtocol); + } } } } diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ServiceController.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ServiceController.java index bfd801a58..92824ed99 100644 --- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ServiceController.java +++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/controller/ServiceController.java @@ -32,6 +32,8 @@ import org.apache.dubbo.admin.service.ProviderService; import org.apache.dubbo.metadata.definition.model.FullServiceDefinition; import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier; +import org.apache.dubbo.registry.Registry; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; @@ -56,6 +58,9 @@ public class ServiceController { private final ConsumerService consumerService; private final Gson gson; + @Autowired + private Registry registry; + @Autowired public ServiceController(ProviderService providerService, ConsumerService consumerService) { this.providerService = providerService; diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/mapping/AdminMappingListener.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/mapping/AdminMappingListener.java index e33575558..42a421902 100644 --- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/mapping/AdminMappingListener.java +++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/mapping/AdminMappingListener.java @@ -18,20 +18,25 @@ package org.apache.dubbo.admin.registry.mapping; import org.apache.dubbo.admin.common.util.Constants; +import org.apache.dubbo.admin.registry.nacos.NacosData; +import org.apache.dubbo.admin.registry.nacos.NacosOpenapiUtil; import org.apache.dubbo.admin.service.impl.InstanceRegistryCache; import org.apache.dubbo.common.URL; import org.apache.dubbo.common.utils.CollectionUtils; import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.metadata.MappingChangedEvent; import org.apache.dubbo.metadata.MappingListener; +import org.apache.dubbo.registry.client.DefaultServiceInstance; import org.apache.dubbo.registry.client.InstanceAddressURL; import org.apache.dubbo.registry.client.ServiceDiscovery; import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent; import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener; +import org.apache.dubbo.registry.nacos.NacosServiceDiscovery; import com.google.common.collect.Sets; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; @@ -39,6 +44,8 @@ import java.util.concurrent.ConcurrentMap; import java.util.stream.Collectors; +import static org.apache.dubbo.admin.common.util.Constants.CATEGORY_KEY; + public class AdminMappingListener implements MappingListener { private static final URL CONSUMER_URL = new URL(Constants.ADMIN_PROTOCOL, NetUtils.getLocalHost(), 0, "", @@ -46,7 +53,7 @@ public class AdminMappingListener implements MappingListener { Constants.GROUP_KEY, Constants.ANY_VALUE, Constants.VERSION_KEY, Constants.ANY_VALUE, Constants.CLASSIFIER_KEY, Constants.ANY_VALUE, - Constants.CATEGORY_KEY, Constants.PROVIDERS_CATEGORY + "," + CATEGORY_KEY, Constants.PROVIDERS_CATEGORY + "," + Constants.CONSUMERS_CATEGORY + "," + Constants.ROUTERS_CATEGORY + "," + Constants.CONFIGURATORS_CATEGORY, @@ -79,13 +86,19 @@ public void onEvent(MappingChangedEvent event) { if (serviceInstancesChangedListener == null) { AddressChangeListener addressChangeListener = new DefaultAddressChangeListener(serviceName, instanceRegistryCache); serviceInstancesChangedListener = new AdminServiceInstancesChangedListener(Sets.newHashSet(serviceName), serviceDiscovery, addressChangeListener); - serviceInstancesChangedListener.setUrl(CONSUMER_URL); +// serviceInstancesChangedListener.setUrl(CONSUMER_URL); + List allInstances = new ArrayList<>(); List serviceInstances = serviceDiscovery.getInstances(serviceName); + if (serviceDiscovery instanceof NacosServiceDiscovery) { + List consumerInstances = convertToInstance(NacosOpenapiUtil.getSubscribeAddressesWithHttpEndpoint(serviceDiscovery.getUrl(), serviceName)); + allInstances.addAll(consumerInstances); + } if (CollectionUtils.isNotEmpty(serviceInstances)) { - serviceInstancesChangedListener.onEvent(new ServiceInstancesChangedEvent(serviceName, serviceInstances)); + allInstances.addAll(serviceInstances); + serviceInstancesChangedListener.onEvent(new ServiceInstancesChangedEvent(serviceName, allInstances)); } serviceListeners.put(serviceName, serviceInstancesChangedListener); - serviceInstancesChangedListener.setUrl(CONSUMER_URL); +// serviceInstancesChangedListener.setUrl(CONSUMER_URL); serviceDiscovery.addServiceInstancesChangedListener(serviceInstancesChangedListener); } } @@ -93,6 +106,15 @@ public void onEvent(MappingChangedEvent event) { } } + private List convertToInstance(List nacosData) { + return nacosData.stream().map(nacos -> { + DefaultServiceInstance instance = new DefaultServiceInstance(); + instance.setHost(nacos.getIp()); + instance.setPort(nacos.getPort()); + return instance; + }).collect(Collectors.toList()); + } + private static class DefaultAddressChangeListener implements AddressChangeListener { private String serviceName; @@ -107,13 +129,22 @@ public DefaultAddressChangeListener(String serviceName, InstanceRegistryCache in @Override public void notifyAddressChanged(String protocolServiceKey, List urls) { String serviceKey = removeProtocol(protocolServiceKey); + ConcurrentMap>> appServiceMap = instanceRegistryCache.computeIfAbsent(Constants.PROVIDERS_CATEGORY, key -> new ConcurrentHashMap<>()); + Map> serviceMap = appServiceMap.computeIfAbsent(serviceName, key -> new ConcurrentHashMap<>()); + Map> consumerServiceMap = instanceRegistryCache.getSubscribedCache().computeIfAbsent(serviceName, key -> new ConcurrentHashMap<>()); + if (CollectionUtils.isEmpty(urls)) { serviceMap.remove(serviceKey); + consumerServiceMap.remove(serviceKey); } else { - List instanceAddressUrls = urls.stream().map(url -> (InstanceAddressURL) url).collect(Collectors.toList()); - serviceMap.put(serviceKey, instanceAddressUrls); + if ("consumer".equals(urls.get(0).getParameter(CATEGORY_KEY))) { + consumerServiceMap.put(serviceKey, urls); + } else { + List instanceAddressUrls = urls.stream().map(url -> (InstanceAddressURL) url).collect(Collectors.toList()); + serviceMap.put(serviceKey, instanceAddressUrls); + } } instanceRegistryCache.refreshConsumer(false); } diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/mapping/impl/NacosServiceMapping.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/mapping/impl/NacosServiceMapping.java index 9633ebb1e..2d40f6748 100644 --- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/mapping/impl/NacosServiceMapping.java +++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/mapping/impl/NacosServiceMapping.java @@ -25,6 +25,7 @@ import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.MappingChangedEvent; import org.apache.dubbo.metadata.MappingListener; +import org.apache.dubbo.registry.client.ServiceInstance; import org.apache.dubbo.registry.nacos.NacosNamingServiceWrapper; import org.apache.dubbo.registry.nacos.util.NacosNamingServiceUtils; @@ -33,6 +34,10 @@ import com.alibaba.nacos.api.naming.pojo.ListView; import com.google.common.collect.Sets; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -156,10 +161,8 @@ private void notifyMappingChangedEvent(String service) { } } - @Override public void addMappingListener(MappingListener listener) { listeners.add(listener); } - -} +} \ No newline at end of file diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/metadata/impl/DubboDelegateMetadataCollector.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/metadata/impl/DubboDelegateMetadataCollector.java new file mode 100644 index 000000000..58cdae839 --- /dev/null +++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/metadata/impl/DubboDelegateMetadataCollector.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.admin.registry.metadata.impl; + +import org.apache.dubbo.admin.registry.metadata.MetaDataCollector; +import org.apache.dubbo.common.URL; +import org.apache.dubbo.metadata.report.MetadataReport; +import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier; + +public class DubboDelegateMetadataCollector implements MetaDataCollector { + private MetadataReport metadataReport; + private URL url; + + public DubboDelegateMetadataCollector(MetadataReport metadataReport) { + this.metadataReport = metadataReport; + } + + @Override + public void setUrl(URL url) { + this.url = url; + } + + @Override + public URL getUrl() { + return url; + } + + @Override + public void init() { + + } + + @Override + public String getProviderMetaData(MetadataIdentifier key) { + return metadataReport.getServiceDefinition(key); + } + + @Override + public String getConsumerMetaData(MetadataIdentifier key) { + return metadataReport.getServiceDefinition(key); + } +} diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/nacos/NacosData.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/nacos/NacosData.java new file mode 100644 index 000000000..e76488841 --- /dev/null +++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/nacos/NacosData.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.admin.registry.nacos; + +public class NacosData { + private String addrStr; + private String agent; + private String app; + private String namespaceId; + private String serviceName; + private String cluster; + private String ip; + private int port; + + public String getAddrStr() { + return addrStr; + } + + public void setAddrStr(String addrStr) { + this.addrStr = addrStr; + } + + public String getAgent() { + return agent; + } + + public void setAgent(String agent) { + this.agent = agent; + } + + public String getApp() { + return app; + } + + public void setApp(String app) { + this.app = app; + } + + public String getNamespaceId() { + return namespaceId; + } + + public void setNamespaceId(String namespaceId) { + this.namespaceId = namespaceId; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public String getCluster() { + return cluster; + } + + public void setCluster(String cluster) { + this.cluster = cluster; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public int getPort() { + return port; + } + + public void setPort(int port) { + this.port = port; + } +} diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/nacos/NacosOpenapiUtil.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/nacos/NacosOpenapiUtil.java new file mode 100644 index 000000000..35bf5095f --- /dev/null +++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/nacos/NacosOpenapiUtil.java @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.admin.registry.nacos; + +import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.utils.StringUtils; + +import com.alibaba.fastjson2.JSON; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.util.Collections; +import java.util.List; + +public class NacosOpenapiUtil { + public static List getSubscribeAddressesWithHttpEndpoint(URL url, String serviceName) { + // 定义Nacos OpenAPI的URL + String nacosUrl = "http://" + url.getAddress() + "/nacos/v1/ns/service/subscribers?serviceName=" + serviceName; + if (StringUtils.isNotEmpty(url.getParameter("namespace"))) { + nacosUrl = nacosUrl + "&namespaceId=" + url.getParameter("namespace"); + } + if (StringUtils.isNotEmpty(url.getParameter("group"))) { + nacosUrl = nacosUrl + "&groupName=" + url.getParameter("group"); + } + + // 创建URL对象 + java.net.URL netUrl = null; + try { + netUrl = new java.net.URL(nacosUrl); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + + HttpURLConnection connection = null; + try { + + // 创建HTTP连接 + connection = (HttpURLConnection) netUrl.openConnection(); + + // 设置请求方法(GET或POST) + connection.setRequestMethod("GET"); + + // 发送请求并获取响应状态码 + int responseCode = connection.getResponseCode(); + + // 读取响应内容 + BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + String line; + StringBuilder response = new StringBuilder(); + while ((line = reader.readLine()) != null) { + response.append(line); + } + reader.close(); + + // 打印响应结果 + System.out.println("Response Code: " + responseCode); + System.out.println("Response Body: " + response.toString()); + + NacosResponse nacosResponse = JSON.parseObject(response.toString(), NacosResponse.class); + return nacosResponse.getSubscribers(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + // 关闭连接 + if (connection != null) { + connection.disconnect(); + } + } + return Collections.EMPTY_LIST; + } +} diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/nacos/NacosResponse.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/nacos/NacosResponse.java new file mode 100644 index 000000000..8b05a9a9a --- /dev/null +++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/registry/nacos/NacosResponse.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.admin.registry.nacos; + +import java.util.List; + +public class NacosResponse { + private int count; + private List subscribers; + + public int getCount() { + return count; + } + + public void setCount(int count) { + this.count = count; + } + + public List getSubscribers() { + return subscribers; + } + + public void setSubscribers(List subscribers) { + this.subscribers = subscribers; + } +} + diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/AdminProviderFirstParams.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/AdminProviderFirstParams.java new file mode 100644 index 000000000..86a4aa2b5 --- /dev/null +++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/AdminProviderFirstParams.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.dubbo.admin.service; + +import org.apache.dubbo.registry.ProviderFirstParams; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_VERSION_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.INTERFACE_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.METHODS_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.RELEASE_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.TAG_KEY; +import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY; + +public class AdminProviderFirstParams implements ProviderFirstParams { + private static final Set PARAMS = Collections.unmodifiableSet(new HashSet() {{ + addAll(Arrays.asList(INTERFACE_KEY, APPLICATION_KEY)); + }}); + @Override + public Set params() { + return PARAMS; + } +} diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/RegistryServerSync.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/RegistryServerSync.java index 886c5b6de..1e94533eb 100644 --- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/RegistryServerSync.java +++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/RegistryServerSync.java @@ -28,6 +28,8 @@ import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.registry.NotifyListener; import org.apache.dubbo.registry.Registry; +import org.apache.dubbo.registry.client.InstanceAddressURL; +import org.apache.dubbo.registry.nacos.NacosRegistry; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.annotation.Autowired; @@ -43,13 +45,15 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import static org.apache.dubbo.admin.common.util.Constants.INTERFACE_KEY; + @Component public class RegistryServerSync implements DisposableBean, NotifyListener { private static final Logger logger = LoggerFactory.getLogger(RegistryServerSync.class); private static final URL SUBSCRIBE = new URL(Constants.ADMIN_PROTOCOL, NetUtils.getLocalHost(), 0, "", - Constants.INTERFACE_KEY, Constants.ANY_VALUE, + INTERFACE_KEY, Constants.ANY_VALUE, Constants.GROUP_KEY, Constants.ANY_VALUE, Constants.VERSION_KEY, Constants.ANY_VALUE, Constants.CLASSIFIER_KEY, Constants.ANY_VALUE, @@ -96,6 +100,9 @@ public void notify(List urls) { final Map>> categories = new HashMap<>(); String interfaceName = null; for (URL url : urls) { + if (url instanceof InstanceAddressURL) { + continue; + } String category = url.getUrlParam().getParameter(Constants.CATEGORY_KEY); if (category == null) { // Assign an initial value to category according to the information in url @@ -175,7 +182,7 @@ public void notify(List urls) { } private String getServiceInterface(URL url) { - String serviceInterface = url.getServiceInterface(); + String serviceInterface = url.getOriginalParameter(INTERFACE_KEY); if (StringUtils.isBlank(serviceInterface) || Constants.ANY_VALUE.equals(serviceInterface)) { serviceInterface = url.getPath(); } diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/impl/ConsumerServiceImpl.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/impl/ConsumerServiceImpl.java index 83950269d..c6163baf5 100644 --- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/impl/ConsumerServiceImpl.java +++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/impl/ConsumerServiceImpl.java @@ -20,10 +20,16 @@ import org.apache.dubbo.admin.common.util.Constants; import org.apache.dubbo.admin.common.util.SyncUtils; import org.apache.dubbo.admin.model.domain.Consumer; +import org.apache.dubbo.admin.registry.nacos.NacosData; +import org.apache.dubbo.admin.registry.nacos.NacosOpenapiUtil; import org.apache.dubbo.admin.service.ConsumerService; import org.apache.dubbo.common.URL; +import org.apache.dubbo.common.utils.NetUtils; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.metadata.report.identifier.MetadataIdentifier; +import org.apache.dubbo.registry.Registry; +import org.apache.dubbo.registry.client.DefaultServiceInstance; +import org.apache.dubbo.registry.nacos.NacosRegistry; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @@ -31,6 +37,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; @Component public class ConsumerServiceImpl extends AbstractService implements ConsumerService { @@ -38,13 +45,38 @@ public class ConsumerServiceImpl extends AbstractService implements ConsumerServ @Autowired private InstanceRegistryQueryHelper instanceRegistryQueryHelper; + @Autowired + private Registry registry; + @Override public List findByService(String service) { List consumers = SyncUtils.url2ConsumerList(findConsumerUrlByService(service)); + if (registry.getUrl().getProtocol().equals("nacos")) { + consumers.addAll(convertToConsumer(NacosOpenapiUtil.getSubscribeAddressesWithHttpEndpoint(registry.getUrl(), getNacosDastaId(service)))); + } consumers.addAll(instanceRegistryQueryHelper.findConsumerByService(service)); return consumers; } + private static String getNacosDastaId(String service) { + // interface:version:group + int len = service.split(":").length; + if (len == 1) { + service += "::"; + } else if (len == 2) { + service += ":"; + } + return service; + } + + private List convertToConsumer(List nacosData) { + return nacosData.stream().map(nacos -> { + Consumer c = new Consumer(); + c.setAddress(nacos.getIp() + ":" + nacos.getPort()); + c.setApplication(nacos.getApp()); + return c; + }).collect(Collectors.toList()); + } @Override public List findAll() { diff --git a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/impl/InstanceRegistryCache.java b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/impl/InstanceRegistryCache.java index 32aa956f2..4a4037ff7 100644 --- a/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/impl/InstanceRegistryCache.java +++ b/dubbo-admin-server/src/main/java/org/apache/dubbo/admin/service/impl/InstanceRegistryCache.java @@ -54,7 +54,7 @@ public class InstanceRegistryCache implements RegistryCache>>> registryCache = new ConcurrentHashMap<>(); - private final Map>> subscribedCache = new ConcurrentHashMap<>(); + private final ConcurrentMap>> subscribedCache = new ConcurrentHashMap<>(); private final AtomicBoolean startRefresh = new AtomicBoolean(false); diff --git a/dubbo-admin-server/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.ProviderFirstParams b/dubbo-admin-server/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.ProviderFirstParams new file mode 100644 index 000000000..52ac297b1 --- /dev/null +++ b/dubbo-admin-server/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.ProviderFirstParams @@ -0,0 +1 @@ +admin=org.apache.dubbo.admin.service.AdminProviderFirstParams diff --git a/dubbo-admin-server/src/main/resources/application.properties b/dubbo-admin-server/src/main/resources/application.properties index 0e3ff31c7..accb327ad 100644 --- a/dubbo-admin-server/src/main/resources/application.properties +++ b/dubbo-admin-server/src/main/resources/application.properties @@ -19,7 +19,7 @@ server.port=38080 dubbo.protocol.port=30880 dubbo.application.qos-port=32222 -# centers in dubbo2.7, if you want to add parameters, please add them to the url +# centers in dubbo, if you want to add parameters, please add them to the url admin.registry.address=zookeeper://127.0.0.1:2181 admin.config-center=zookeeper://127.0.0.1:2181 admin.metadata-report.address=zookeeper://127.0.0.1:2181 diff --git a/pom.xml b/pom.xml index f7ae40950..1dffc6f60 100644 --- a/pom.xml +++ b/pom.xml @@ -56,9 +56,9 @@ 0.5.0-SNAPSHOT ${project.basedir} 3.7 - 3.1.6 + 3.2.5 1.2.83 - 2.10.0 + 2.9.2 0.8.2 1.2.0 20.0