Polish alibaba/spring-cloud-alibaba#1529 : spring-cloud-starter-dubbo消费者订阅服务时No provider available from registry localhost:9090

pull/1684/head
mercyblitz 4 years ago
parent f34a8a13c6
commit 9b4975ad2a

@ -17,20 +17,21 @@
package com.alibaba.cloud.dubbo.autoconfigure;
import java.util.Collection;
import java.util.Optional;
import java.util.function.Supplier;
import com.alibaba.cloud.dubbo.metadata.DubboProtocolConfigSupplier;
import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;
import com.alibaba.cloud.dubbo.metadata.repository.MetadataServiceInstanceSelector;
import com.alibaba.cloud.dubbo.metadata.repository.RandomServiceInstanceSelector;
import com.alibaba.cloud.dubbo.metadata.repository.ServiceInstanceSelector;
import com.alibaba.cloud.dubbo.metadata.resolver.DubboServiceBeanMetadataResolver;
import com.alibaba.cloud.dubbo.metadata.resolver.MetadataResolver;
import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceExporter;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy;
import com.alibaba.cloud.dubbo.service.IntrospectiveDubboMetadataService;
import com.alibaba.cloud.dubbo.util.DubboMetadataUtils;
import com.alibaba.cloud.dubbo.util.JSONUtils;
import feign.Contract;
import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.spring.ServiceBean;
import org.apache.dubbo.config.spring.context.event.ServiceBeanExportedEvent;
@ -44,7 +45,6 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.util.CollectionUtils;
/**
* Spring Boot Auto-Configuration class for Dubbo Metadata.
@ -53,7 +53,8 @@ import org.springframework.util.CollectionUtils;
*/
@Configuration(proxyBeanMethods = false)
@Import({ DubboServiceMetadataRepository.class, IntrospectiveDubboMetadataService.class,
DubboMetadataServiceExporter.class, JSONUtils.class })
DubboMetadataServiceExporter.class, JSONUtils.class,
DubboMetadataServiceProxy.class, DubboMetadataUtils.class })
public class DubboMetadataAutoConfiguration {
@Autowired
@ -73,9 +74,8 @@ public class DubboMetadataAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public MetadataServiceInstanceSelector metadataServiceInstanceSelector() {
return serviceInstances -> CollectionUtils.isEmpty(serviceInstances)
? Optional.empty() : serviceInstances.stream().findAny();
public ServiceInstanceSelector metadataServiceInstanceSelector() {
return new RandomServiceInstanceSelector();
}
@Bean
@ -84,15 +84,7 @@ public class DubboMetadataAutoConfiguration {
return new DubboProtocolConfigSupplier(protocols);
}
@Bean
@ConditionalOnMissingBean
public DubboMetadataServiceProxy dubboMetadataConfigServiceProxy(
DubboGenericServiceFactory factory) {
return new DubboMetadataServiceProxy(factory);
}
// Event-Handling
@EventListener(ServiceBeanExportedEvent.class)
public void onServiceBeanExported(ServiceBeanExportedEvent event) {
ServiceBean serviceBean = event.getServiceBean();

@ -56,6 +56,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
@ -97,12 +98,11 @@ import static org.springframework.util.StringUtils.hasText;
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(name = "org.springframework.cloud.client.discovery.DiscoveryClient")
@ConditionalOnProperty(name = "spring.cloud.discovery.enabled", matchIfMissing = true)
@AutoConfigureAfter(
name = { EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME,
ZOOKEEPER_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME,
CONSUL_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME,
NACOS_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME },
value = { DubboServiceRegistrationAutoConfiguration.class })
@AutoConfigureAfter(name = { EUREKA_CLIENT_AUTO_CONFIGURATION_CLASS_NAME,
ZOOKEEPER_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME,
CONSUL_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME,
NACOS_DISCOVERY_AUTO_CONFIGURATION_CLASS_NAME }, value = {
DubboServiceRegistrationAutoConfiguration.class })
public class DubboServiceDiscoveryAutoConfiguration {
/**
@ -135,6 +135,9 @@ public class DubboServiceDiscoveryAutoConfiguration {
*/
private final ObjectProvider<Predicate<HeartbeatEvent>> heartbeatEventChangedPredicate;
@Value("${spring.application.name:${dubbo.application.name:application}}")
private String currentApplicationName;
public DubboServiceDiscoveryAutoConfiguration(
DubboServiceMetadataRepository dubboServiceMetadataRepository,
ApplicationEventPublisher applicationEventPublisher,
@ -154,10 +157,12 @@ public class DubboServiceDiscoveryAutoConfiguration {
* NotifyListener)
*/
private void dispatchServiceInstancesChangedEvent(String serviceName,
Collection<ServiceInstance> serviceInstances) {
if (!hasText(serviceName) || serviceInstances == null) {
List<ServiceInstance> serviceInstances) {
if (!hasText(serviceName) || Objects.equals(currentApplicationName, serviceName)
|| serviceInstances == null) {
return;
}
ServiceInstancesChangedEvent event = new ServiceInstancesChangedEvent(serviceName,
serviceInstances);
if (logger.isInfoEnabled()) {

@ -23,6 +23,7 @@ import java.util.Set;
import org.springframework.boot.context.properties.ConfigurationProperties;
import static com.alibaba.cloud.dubbo.util.DubboCloudConstants.CONFIG_PROPERTY_PREFIX;
import static com.alibaba.cloud.dubbo.util.DubboCloudConstants.DUBBO_CLOUD_REGISTRY_PROPERTY_VALUE;
import static org.springframework.util.StringUtils.commaDelimitedListToStringArray;
import static org.springframework.util.StringUtils.hasText;
import static org.springframework.util.StringUtils.trimAllWhitespace;
@ -48,6 +49,8 @@ public class DubboCloudProperties {
*/
private String subscribedServices = ALL_DUBBO_SERVICES;
private String registryType = DUBBO_CLOUD_REGISTRY_PROPERTY_VALUE;
public String getSubscribedServices() {
return subscribedServices;
}
@ -80,4 +83,11 @@ public class DubboCloudProperties {
return Collections.unmodifiableSet(subscribedServices);
}
public String getRegistryType() {
return registryType;
}
public void setRegistryType(String registryType) {
this.registryType = registryType;
}
}

@ -22,9 +22,7 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.PostConstruct;
@ -38,10 +36,13 @@ import com.alibaba.cloud.dubbo.registry.event.SubscribedServicesChangedEvent;
import com.alibaba.cloud.dubbo.service.DubboMetadataService;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceExporter;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy;
import com.alibaba.cloud.dubbo.util.DubboMetadataUtils;
import com.alibaba.cloud.dubbo.util.JSONUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.apache.dubbo.common.URL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -61,15 +62,15 @@ import org.springframework.util.MultiValueMap;
import static com.alibaba.cloud.dubbo.env.DubboCloudProperties.ALL_DUBBO_SERVICES;
import static com.alibaba.cloud.dubbo.http.DefaultHttpRequest.builder;
import static java.lang.String.format;
import static java.lang.String.valueOf;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptySet;
import static java.util.Collections.unmodifiableList;
import static java.util.Collections.unmodifiableMap;
import static java.util.Collections.unmodifiableSet;
import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_PREFIX;
import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URLS_PROPERTY_NAME;
import static org.springframework.util.CollectionUtils.isEmpty;
import static org.springframework.util.StringUtils.hasText;
@ -85,14 +86,15 @@ public class DubboServiceMetadataRepository
/**
* The prefix of {@link DubboMetadataService} : "dubbo.metadata-service.".
*/
public static final String DUBBO_METADATA_SERVICE_PREFIX = "dubbo.metadata-service.";
@Deprecated
public static final String DUBBO_METADATA_SERVICE_PREFIX = METADATA_SERVICE_PREFIX;
/**
* The {@link URL URLs} property name of {@link DubboMetadataService} :
* "dubbo.metadata-service.urls".
*/
public static final String DUBBO_METADATA_SERVICE_URLS_PROPERTY_NAME = DUBBO_METADATA_SERVICE_PREFIX
+ "urls";
@Deprecated
public static final String DUBBO_METADATA_SERVICE_URLS_PROPERTY_NAME = METADATA_SERVICE_URLS_PROPERTY_NAME;
/**
* The {@link String#format(String, Object...) pattern} of dubbo protocols port.
@ -108,11 +110,6 @@ public class DubboServiceMetadataRepository
*/
private final Object monitor = new Object();
/**
* A {@link Set} of service names that had been initialized.
*/
private final Set<String> initializedServices = new LinkedHashSet<>();
/**
* All exported {@link URL urls} {@link Map} whose key is the return value of
* {@link URL#getServiceKey()} method and value is the {@link List} of {@link URL
@ -122,12 +119,6 @@ public class DubboServiceMetadataRepository
// =================================== Registration
// =================================== //
/**
* The subscribed {@link URL urls} {@link Map} of {@link DubboMetadataService}, whose
* key is the return value of {@link URL#getServiceKey()} method and value is the
* {@link List} of {@link URL URLs}.
*/
private final MultiValueMap<String, URL> subscribedDubboMetadataServiceURLs = new LinkedMultiValueMap<>();
// ====================================================================================
// //
@ -172,7 +163,7 @@ public class DubboServiceMetadataRepository
private DiscoveryClient discoveryClient;
@Autowired
private MetadataServiceInstanceSelector metadataServiceInstanceSelector;
private ServiceInstanceSelector serviceInstanceSelector;
@Autowired
private JSONUtils jsonUtils;
@ -180,6 +171,9 @@ public class DubboServiceMetadataRepository
@Autowired
private InetUtils inetUtils;
@Autowired
private DubboMetadataUtils dubboMetadataUtils;
@Value("${spring.application.name}")
private String currentApplicationName;
@ -275,28 +269,11 @@ public class DubboServiceMetadataRepository
* @param serviceName service of name
*/
public void initializeMetadata(String serviceName) {
synchronized (monitor) {
if (initializedServices.contains(serviceName)) {
if (logger.isDebugEnabled()) {
logger.debug(
"The metadata of Dubbo service[name : {}] has been initialized",
serviceName);
}
}
else {
if (logger.isInfoEnabled()) {
logger.info(
"The metadata of Dubbo service[name : {}] is about to be initialized",
serviceName);
}
if (initSubscribedDubboMetadataService(serviceName)) {
// mark this service name having been initialized
initializedServices.add(serviceName);
}
initDubboRestServiceMetadataRepository(serviceName);
}
}
}
private DubboMetadataService getProxy(String serviceName) {
return dubboMetadataConfigServiceProxy.getProxy(serviceName);
}
/**
@ -307,15 +284,8 @@ public class DubboServiceMetadataRepository
*/
public void removeMetadataAndInitializedService(String serviceName, URL url) {
synchronized (monitor) {
initializedServices.remove(serviceName);
dubboMetadataConfigServiceProxy.removeProxy(serviceName);
dubboRestServiceMetadataRepository.remove(serviceName);
// fix #1260 if the subscribedDubboMetadataServiceURLs removed failold meta
// information will be retained
if (DubboMetadataService.class.getName().equals(url.getServiceInterface())) {
String serviceKey = url.getServiceKey();
subscribedDubboMetadataServiceURLs.remove(serviceKey);
}
}
}
@ -345,8 +315,7 @@ public class DubboServiceMetadataRepository
private void addDubboMetadataServiceURLsMetadata(Map<String, String> metadata,
List<URL> dubboMetadataServiceURLs) {
String dubboMetadataServiceURLsJSON = jsonUtils.toJSON(dubboMetadataServiceURLs);
metadata.put(DUBBO_METADATA_SERVICE_URLS_PROPERTY_NAME,
dubboMetadataServiceURLsJSON);
metadata.put(METADATA_SERVICE_URLS_PROPERTY_NAME, dubboMetadataServiceURLsJSON);
}
private void addDubboProtocolsPortMetadata(Map<String, String> metadata) {
@ -359,15 +328,6 @@ public class DubboServiceMetadataRepository
});
}
/**
* Get the property name of Dubbo Protocol.
* @param protocol Dubbo Protocol
* @return non-null
*/
public String getDubboProtocolPropertyName(String protocol) {
return format(DUBBO_PROTOCOLS_PORT_PROPERTY_NAME_PATTERN, protocol);
}
/**
* Publish the {@link Set} of {@link ServiceRestMetadata}.
* @param serviceRestMetadataSet the {@link Set} of {@link ServiceRestMetadata}
@ -389,25 +349,6 @@ public class DubboServiceMetadataRepository
return unmodifiableSet(serviceRestMetadata);
}
public List<URL> findSubscribedDubboMetadataServiceURLs(String serviceName,
String group, String version, String protocol) {
String serviceKey = URL.buildKey(serviceName, group, version);
List<URL> urls = null;
synchronized (monitor) {
urls = subscribedDubboMetadataServiceURLs.get(serviceKey);
}
if (isEmpty(urls)) {
return emptyList();
}
return hasText(protocol) ? urls.stream()
.filter(url -> url.getProtocol().equalsIgnoreCase(protocol))
.collect(Collectors.toList()) : unmodifiableList(urls);
}
/**
* The specified service is subscribe or not.
* @param serviceName the service name
@ -457,24 +398,13 @@ public class DubboServiceMetadataRepository
return allExportedURLs.keySet();
}
/**
* Get the {@link URL urls} that {@link DubboMetadataService} exported by the
* specified {@link ServiceInstance}.
* @param serviceInstance {@link ServiceInstance}
* @return the mutable {@link URL urls}
*/
public List<URL> getDubboMetadataServiceURLs(ServiceInstance serviceInstance) {
Map<String, String> metadata = serviceInstance.getMetadata();
String dubboURLsJSON = metadata.get(DUBBO_METADATA_SERVICE_URLS_PROPERTY_NAME);
return jsonUtils.toURLs(dubboURLsJSON);
}
public Integer getDubboProtocolPort(ServiceInstance serviceInstance,
String protocol) {
String protocolProperty = getDubboProtocolPropertyName(protocol);
Map<String, String> metadata = serviceInstance.getMetadata();
String protocolPort = metadata.get(protocolProperty);
return hasText(protocolPort) ? Integer.valueOf(protocolPort) : null;
return dubboMetadataUtils.getDubboProtocolPort(serviceInstance, protocol);
}
private String getDubboProtocolPropertyName(String protocol) {
return dubboMetadataUtils.getDubboProtocolPropertyName(protocol);
}
public List<URL> getExportedURLs(String serviceInterface, String group,
@ -490,6 +420,11 @@ public class DubboServiceMetadataRepository
protected void initDubboRestServiceMetadataRepository(String serviceName) {
if (dubboRestServiceMetadataRepository.containsKey(serviceName)) {
if (logger.isDebugEnabled()) {
logger.debug(
"The metadata of Dubbo service[name : {}] has been initialized",
serviceName);
}
return;
}
@ -598,8 +533,7 @@ public class DubboServiceMetadataRepository
Set<ServiceRestMetadata> metadata = emptySet();
DubboMetadataService dubboMetadataService = dubboMetadataConfigServiceProxy
.getProxy(serviceName);
DubboMetadataService dubboMetadataService = getProxy(serviceName);
if (dubboMetadataService != null) {
try {
@ -624,68 +558,29 @@ public class DubboServiceMetadataRepository
subscribedServices.remove(currentApplicationName);
}
protected Boolean initSubscribedDubboMetadataService(String serviceName) {
// this need to judge whether the initialization is successful or not. The failed
// initialization will not change the initializedServices
Optional<ServiceInstance> optionalServiceInstance = metadataServiceInstanceSelector
.choose(discoveryClient.getInstances(serviceName));
if (!((Optional) optionalServiceInstance).isPresent()) {
return false;
}
ServiceInstance serviceInstance = optionalServiceInstance.get();
if (null == serviceInstance) {
return false;
}
List<URL> dubboMetadataServiceURLs = getDubboMetadataServiceURLs(serviceInstance);
if (dubboMetadataServiceURLs.isEmpty()) {
return false;
}
for (URL dubboMetadataServiceURL : dubboMetadataServiceURLs) {
try {
initSubscribedDubboMetadataServiceURL(dubboMetadataServiceURL);
DubboMetadataService dubboMetadataService = dubboMetadataConfigServiceProxy
.getProxy(serviceName);
if (dubboMetadataService == null) {
dubboMetadataService = initDubboMetadataServiceProxy(
dubboMetadataServiceURL);
}
if (dubboMetadataService == null) {
removeMetadataAndInitializedService(serviceName,
dubboMetadataServiceURL);
return false;
}
}
catch (Throwable e) {
if (logger.isErrorEnabled()) {
logger.error(e.getMessage(), e);
}
return false;
}
}
initDubboRestServiceMetadataRepository(serviceName);
return true;
}
private void initSubscribedDubboMetadataServiceURL(URL dubboMetadataServiceURL) {
// add subscriptions
String serviceKey = dubboMetadataServiceURL.getServiceKey();
subscribedDubboMetadataServiceURLs.add(serviceKey, dubboMetadataServiceURL);
}
private DubboMetadataService initDubboMetadataServiceProxy(
URL dubboMetadataServiceURL) {
String serviceName = dubboMetadataServiceURL.getParameter(APPLICATION_KEY);
String version = dubboMetadataServiceURL.getParameter(VERSION_KEY);
// Initialize DubboMetadataService with right version
return dubboMetadataConfigServiceProxy.initProxy(serviceName, version);
}
@Override
public void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
public List<URL> findSubscribedDubboMetadataServiceURLs(URL subscribedURL) {
// The parameter of "group" as the service name
String serviceInterface = subscribedURL.getServiceInterface();
String group = subscribedURL.getParameter(GROUP_KEY);
String version = subscribedURL.getParameter(VERSION_KEY);
String protocol = subscribedURL.getParameter(PROTOCOL_KEY);
List<ServiceInstance> serviceInstances = discoveryClient.getInstances(group);
List<URL> urls = dubboMetadataUtils.getDubboMetadataServiceURLs(serviceInstances,
serviceInterface, version, protocol);
if (logger.isInfoEnabled()) {
logger.info(
"The DubboMetadataService of service [name : {} , instances : {}] URLs[protocol : {} , size : {}] has been subscribed.",
group, serviceInstances.size(), protocol, urls.size());
}
return urls;
}
}

@ -0,0 +1,43 @@
/*
* 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 com.alibaba.cloud.dubbo.metadata.repository;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ThreadLocalRandom;
import org.springframework.cloud.client.ServiceInstance;
import static java.util.Optional.of;
import static org.springframework.util.CollectionUtils.isEmpty;
/**
* Random {@link ServiceInstanceSelector}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class RandomServiceInstanceSelector implements ServiceInstanceSelector {
@Override
public Optional<ServiceInstance> select(List<ServiceInstance> serviceInstances) {
if (isEmpty(serviceInstances)) {
return Optional.empty();
}
ThreadLocalRandom random = ThreadLocalRandom.current();
return of(serviceInstances.get(random.nextInt(serviceInstances.size())));
}
}

@ -26,13 +26,13 @@ import org.springframework.cloud.client.ServiceInstance;
*
* @author <a href="mailto:liuxx-u@outlook.com">liuxx</a>
*/
public interface MetadataServiceInstanceSelector {
public interface ServiceInstanceSelector {
/**
* choose a service instance to get metadata.
* Select a service instance to get metadata.
* @param serviceInstances all service instance
* @return the service instance to get metadata
*/
Optional<ServiceInstance> choose(List<ServiceInstance> serviceInstances);
Optional<ServiceInstance> select(List<ServiceInstance> serviceInstances);
}

@ -63,6 +63,7 @@ import static org.springframework.util.StringUtils.hasText;
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@Deprecated
public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
/**
@ -370,12 +371,7 @@ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
}
private void subscribeDubboMetadataServiceURLs(URL url, NotifyListener listener) {
String serviceInterface = url.getServiceInterface();
String group = url.getParameter(GROUP_KEY);
String version = url.getParameter(VERSION_KEY);
String protocol = url.getParameter(PROTOCOL_KEY);
List<URL> urls = repository.findSubscribedDubboMetadataServiceURLs(
serviceInterface, group, version, protocol);
List<URL> urls = repository.findSubscribedDubboMetadataServiceURLs(url);
listener.notify(urls);
}

@ -0,0 +1,499 @@
/*
* 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 com.alibaba.cloud.dubbo.registry;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;
import com.alibaba.cloud.dubbo.registry.event.ServiceInstancesChangedEvent;
import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory;
import com.alibaba.cloud.dubbo.service.DubboMetadataService;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy;
import com.alibaba.cloud.dubbo.util.DubboMetadataUtils;
import com.alibaba.cloud.dubbo.util.JSONUtils;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.URLBuilder;
import org.apache.dubbo.metadata.MetadataService;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.support.FailbackRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.CollectionUtils;
import static java.lang.String.format;
import static java.util.Collections.emptyList;
import static java.util.Collections.singleton;
import static java.util.stream.StreamSupport.stream;
import static org.apache.dubbo.common.URLBuilder.from;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PID_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER;
import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE;
import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL;
import static org.apache.dubbo.common.utils.CollectionUtils.isEmpty;
import static org.apache.dubbo.registry.Constants.ADMIN_PROTOCOL;
import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URLS_PROPERTY_NAME;
import static org.springframework.util.StringUtils.hasText;
/**
* Dubbo Cloud {@link FailbackRegistry} is based on Spring Cloud {@link DiscoveryClient}
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class DubboCloudRegistry extends FailbackRegistry {
/**
* The parameter name of {@link #servicesLookupInterval}.
*/
public static final String SERVICES_LOOKUP_INTERVAL_PARAM_NAME = "dubbo.services.lookup.interval";
protected static final String DUBBO_METADATA_SERVICE_CLASS_NAME = DubboMetadataService.class
.getName();
/**
* Caches the IDs of {@link ApplicationListener}.
*/
private static final Set<String> registerListeners = new HashSet<>();
protected final Logger logger = LoggerFactory.getLogger(getClass());
private final DiscoveryClient discoveryClient;
private final DubboServiceMetadataRepository repository;
private final DubboMetadataServiceProxy dubboMetadataConfigServiceProxy;
private final JSONUtils jsonUtils;
private final DubboGenericServiceFactory dubboGenericServiceFactory;
private final DubboMetadataUtils dubboMetadataUtils;
/**
* The interval in second of lookup service names(only for Dubbo-OPS).
*/
private final long servicesLookupInterval;
private final ConfigurableApplicationContext applicationContext;
private final String currentApplicationName;
public DubboCloudRegistry(URL url, DiscoveryClient discoveryClient,
DubboServiceMetadataRepository repository,
DubboMetadataServiceProxy dubboMetadataConfigServiceProxy,
JSONUtils jsonUtils, DubboGenericServiceFactory dubboGenericServiceFactory,
ConfigurableApplicationContext applicationContext) {
super(url);
this.servicesLookupInterval = url
.getParameter(SERVICES_LOOKUP_INTERVAL_PARAM_NAME, 60L);
this.discoveryClient = discoveryClient;
this.repository = repository;
this.dubboMetadataConfigServiceProxy = dubboMetadataConfigServiceProxy;
this.jsonUtils = jsonUtils;
this.dubboGenericServiceFactory = dubboGenericServiceFactory;
this.applicationContext = applicationContext;
this.dubboMetadataUtils = getBean(DubboMetadataUtils.class);
this.currentApplicationName = dubboMetadataUtils.getCurrentApplicationName();
}
private <T> T getBean(Class<T> beanClass) {
return this.applicationContext.getBean(beanClass);
}
protected boolean shouldRegister(URL url) {
String side = url.getParameter(SIDE_KEY);
boolean should = PROVIDER_SIDE.equals(side); // Only register the Provider.
if (!should) {
if (logger.isDebugEnabled()) {
logger.debug("The URL[{}] should not be registered.", url.toString());
}
}
return should;
}
@Override
public final void doRegister(URL url) {
if (!shouldRegister(url)) {
return;
}
repository.exportURL(url);
}
@Override
public final void doUnregister(URL url) {
if (!shouldRegister(url)) {
return;
}
repository.unexportURL(url);
}
@Override
public final void doSubscribe(URL url, NotifyListener listener) {
if (isAdminURL(url)) {
// TODO in future
if (logger.isWarnEnabled()) {
logger.warn("This feature about admin will be supported in the future.");
}
}
else if (isDubboMetadataServiceURL(url)) { // for DubboMetadataService
subscribeDubboMetadataServiceURLs(url, listener);
}
else { // for general Dubbo Services
subscribeURLs(url, listener);
}
}
private void subscribeURLs(URL url, NotifyListener listener) {
// Sync subscription
subscribeURLs(url, getServices(url), listener);
// Async subscription
registerServiceInstancesChangedListener(url, event -> {
Set<String> serviceNames = getServices(url);
String serviceName = event.getServiceName();
if (serviceNames.contains(serviceName)) {
subscribeURLs(url, serviceNames, listener);
}
});
}
private void subscribeURLs(URL url, Set<String> serviceNames,
NotifyListener listener) {
List<URL> subscribedURLs = new LinkedList<>();
serviceNames.forEach(serviceName -> {
subscribeURLs(url, subscribedURLs, serviceName,
() -> getServiceInstances(serviceName));
});
// Notify all
notifyAllSubscribedURLs(url, subscribedURLs, listener);
}
private void registerServiceInstancesChangedListener(URL url,
ApplicationListener<ServiceInstancesChangedEvent> listener) {
String listenerId = generateId(url);
if (registerListeners.add(listenerId)) {
applicationContext.addApplicationListener(listener);
}
}
private void subscribeURLs(URL subscribedURL, List<URL> subscribedURLs,
String serviceName,
Supplier<List<ServiceInstance>> serviceInstancesSupplier) {
List<ServiceInstance> serviceInstances = serviceInstancesSupplier.get();
subscribeURLs(subscribedURL, subscribedURLs, serviceName, serviceInstances);
}
private void subscribeURLs(URL subscribedURL, List<URL> subscribedURLs,
String serviceName, List<ServiceInstance> serviceInstances) {
if (CollectionUtils.isEmpty(serviceInstances)) {
if (logger.isWarnEnabled()) {
logger.warn(format("There is no instance in service[name : %s]",
serviceName));
}
}
List<URL> exportedURLs = getExportedURLs(subscribedURL, serviceName,
serviceInstances);
/**
* Add the exported URLs from {@link MetadataService}
*/
subscribedURLs.addAll(exportedURLs);
}
private List<URL> getExportedURLs(URL subscribedURL, String serviceName,
List<ServiceInstance> serviceInstances) {
List<ServiceInstance> validServiceInstances = filter(serviceInstances);
// If there is no valid ServiceInstance, return empty result
if (isEmpty(validServiceInstances)) {
if (logger.isWarnEnabled()) {
logger.warn(
"There is no instance from service[name : {}], and then Dubbo Service[key : {}] will not be "
+ "available , please make sure the further impact",
serviceName, subscribedURL.getServiceKey());
}
return emptyList();
}
List<URL> subscribedURLs = cloneExportedURLs(subscribedURL, serviceInstances);
// clear local service instances, help GC
validServiceInstances.clear();
return subscribedURLs;
}
/**
* Clone the subscribed URLs based on the template URLs
* @param subscribedURL the URL to be subscribed
* @param serviceInstances the list of {@link ServiceInstance service instances}
* @return non-null
*/
private List<URL> cloneExportedURLs(URL subscribedURL,
List<ServiceInstance> serviceInstances) {
List<URL> clonedExportedURLs = new LinkedList<>();
serviceInstances.forEach(serviceInstance -> {
String host = serviceInstance.getHost();
getTemplateExportedURLs(subscribedURL, serviceInstances).stream()
.map(templateURL -> templateURL.removeParameter(TIMESTAMP_KEY))
.map(templateURL -> templateURL.removeParameter(PID_KEY))
.map(templateURL -> {
String protocol = templateURL.getProtocol();
int port = repository.getDubboProtocolPort(serviceInstance,
protocol);
if (Objects.equals(templateURL.getHost(), host)
&& Objects.equals(templateURL.getPort(), port)) { // use
// templateURL
// if
// equals
return templateURL;
}
URLBuilder clonedURLBuilder = from(templateURL) // remove the
// parameters from
// the template
// URL
.setHost(host) // reset the host
.setPort(port); // reset the port
return clonedURLBuilder.build();
}).forEach(clonedExportedURLs::add);
});
return clonedExportedURLs;
}
private List<URL> getTemplateExportedURLs(URL subscribedURL,
List<ServiceInstance> serviceInstances) {
DubboMetadataService dubboMetadataService = getProxy(serviceInstances);
List<URL> templateExportedURLs = emptyList();
if (dubboMetadataService != null) {
templateExportedURLs = getExportedURLs(dubboMetadataService, subscribedURL);
}
else {
if (logger.isWarnEnabled()) {
logger.warn(
"The metadata of Dubbo service[key : {}] still can't be found, it could effect the further "
+ "Dubbo service invocation",
subscribedURL.getServiceKey());
}
}
return templateExportedURLs;
}
private DubboMetadataService getProxy(List<ServiceInstance> serviceInstances) {
return dubboMetadataConfigServiceProxy.getProxy(serviceInstances);
}
private List<ServiceInstance> filter(Collection<ServiceInstance> serviceInstances) {
return serviceInstances.stream().filter(this::isDubboServiceInstance)
.collect(Collectors.toList());
}
private boolean isDubboServiceInstance(ServiceInstance serviceInstance) {
Map<String, String> metadata = serviceInstance.getMetadata();
return metadata.containsKey(METADATA_SERVICE_URLS_PROPERTY_NAME);
}
private Set<String> getServices(URL url) {
Set<String> subscribedServices = repository.getSubscribedServices();
// TODO Add the filter feature
return subscribedServices;
}
private void notifyAllSubscribedURLs(URL url, List<URL> subscribedURLs,
NotifyListener listener) {
if (isEmpty(subscribedURLs)) {
// Add the EMPTY_PROTOCOL URL
subscribedURLs.add(emptyURL(url));
if (isDubboMetadataServiceURL(url)) {
// if meta service change, and serviceInstances is zero, will clean up
// information about this client
String serviceName = url.getParameter(GROUP_KEY);
repository.removeMetadataAndInitializedService(serviceName, url);
}
}
if (logger.isDebugEnabled()) {
logger.debug("The subscribed URL[{}] will notify all URLs : {}", url,
subscribedURLs);
}
// Notify all
listener.notify(subscribedURLs);
}
private List<ServiceInstance> getServiceInstances(Iterable<String> serviceNames) {
return stream(serviceNames.spliterator(), false).map(this::getServiceInstances)
.flatMap(Collection::stream).collect(Collectors.toList());
}
private List<ServiceInstance> getServiceInstances(String serviceName) {
return hasText(serviceName) ? doGetServiceInstances(serviceName) : emptyList();
}
private List<ServiceInstance> doGetServiceInstances(String serviceName) {
List<ServiceInstance> serviceInstances = emptyList();
try {
serviceInstances = discoveryClient.getInstances(serviceName);
}
catch (Exception e) {
if (logger.isErrorEnabled()) {
logger.error(e.getMessage(), e);
}
}
return serviceInstances;
}
private String generateId(URL url) {
return url.getServiceKey();
}
private URL emptyURL(URL url) {
// issue : When the last service provider is closed, the client still periodically
// connects to the last provider.n
// fix https://github.com/alibaba/spring-cloud-alibaba/issues/1259
return from(url).setProtocol(EMPTY_PROTOCOL).removeParameter(CATEGORY_KEY)
.build();
}
private List<URL> getExportedURLs(DubboMetadataService dubboMetadataService,
URL subscribedURL) {
String serviceInterface = subscribedURL.getServiceInterface();
String group = subscribedURL.getParameter(GROUP_KEY);
String version = subscribedURL.getParameter(VERSION_KEY);
// The subscribed protocol may be null
String subscribedProtocol = subscribedURL.getParameter(PROTOCOL_KEY);
String exportedURLsJSON = dubboMetadataService.getExportedURLs(serviceInterface,
group, version);
return jsonUtils.toURLs(exportedURLsJSON).stream()
.filter(exportedURL -> subscribedProtocol == null
|| subscribedProtocol.equalsIgnoreCase(exportedURL.getProtocol()))
.collect(Collectors.toList());
}
private void subscribeDubboMetadataServiceURLs(URL subscribedURL,
NotifyListener listener) {
// Sync subscription
subscribeDubboMetadataServiceURLs(subscribedURL, listener,
singleton(getServiceName(subscribedURL)));
// Sync subscription
if (containsProviderCategory(subscribedURL)) {
registerServiceInstancesChangedListener(subscribedURL, event -> {
Set<String> serviceNames = getServices(subscribedURL);
if (!serviceNames.contains(event.getServiceName())) {
return;
}
subscribeDubboMetadataServiceURLs(subscribedURL, listener, serviceNames);
});
}
}
private String getServiceName(URL subscribedURL) {
return subscribedURL.getParameter(GROUP_KEY);
}
private void subscribeDubboMetadataServiceURLs(URL subscribedURL,
NotifyListener listener, Set<String> serviceNames) {
String serviceInterface = subscribedURL.getServiceInterface();
String version = subscribedURL.getParameter(VERSION_KEY);
String protocol = subscribedURL.getParameter(PROTOCOL_KEY);
List<ServiceInstance> serviceInstances = getServiceInstances(serviceNames);
List<URL> urls = dubboMetadataUtils.getDubboMetadataServiceURLs(serviceInstances,
serviceInterface, version, protocol);
notifyAllSubscribedURLs(subscribedURL, urls, listener);
}
private boolean containsProviderCategory(URL subscribedURL) {
String category = subscribedURL.getParameter(CATEGORY_KEY);
return category == null ? false : category.contains(PROVIDER);
}
@Override
public final void doUnsubscribe(URL url, NotifyListener listener) {
// TODO
}
@Override
public boolean isAvailable() {
return !discoveryClient.getServices().isEmpty();
}
protected boolean isAdminURL(URL url) {
return ADMIN_PROTOCOL.equals(url.getProtocol());
}
protected boolean isDubboMetadataServiceURL(URL url) {
return DUBBO_METADATA_SERVICE_CLASS_NAME.equals(url.getServiceInterface());
}
}

@ -31,7 +31,10 @@ import org.springframework.context.ConfigurableApplicationContext;
* protocol is "spring-cloud".
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
* @deprecated It's a legacy and not recommended implementation, being replacing to be
* {@link DubboCloudRegistry}
*/
@Deprecated
public class SpringCloudRegistry extends AbstractSpringCloudRegistry {
private final DubboServiceMetadataRepository dubboServiceMetadataRepository;

@ -16,10 +16,12 @@
package com.alibaba.cloud.dubbo.registry;
import com.alibaba.cloud.dubbo.env.DubboCloudProperties;
import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;
import com.alibaba.cloud.dubbo.service.DubboGenericServiceFactory;
import com.alibaba.cloud.dubbo.service.DubboMetadataServiceProxy;
import com.alibaba.cloud.dubbo.util.JSONUtils;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.registry.Registry;
import org.apache.dubbo.registry.RegistryFactory;
@ -28,6 +30,7 @@ import org.apache.dubbo.registry.support.AbstractRegistryFactory;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.ConfigurableApplicationContext;
import static com.alibaba.cloud.dubbo.util.DubboCloudConstants.SPRING_CLOUD_REGISTRY_PROPERTY_VALUE;
import static java.lang.System.getProperty;
/**
@ -50,6 +53,8 @@ public class SpringCloudRegistryFactory extends AbstractRegistryFactory {
*/
public static String ADDRESS = "localhost";
public static String MODE = "dubbo.cloud.";
private static String SERVICES_LOOKUP_SCHEDULER_THREAD_NAME_PREFIX = getProperty(
"dubbo.services.lookup.scheduler.thread.name.prefix ",
"dubbo-services-lookup-");
@ -88,9 +93,26 @@ public class SpringCloudRegistryFactory extends AbstractRegistryFactory {
@Override
public Registry createRegistry(URL url) {
init();
return new SpringCloudRegistry(url, discoveryClient,
dubboServiceMetadataRepository, dubboMetadataConfigServiceProxy,
jsonUtils, dubboGenericServiceFactory, applicationContext);
DubboCloudProperties dubboCloudProperties = applicationContext
.getBean(DubboCloudProperties.class);
Registry registry = null;
switch (dubboCloudProperties.getRegistryType()) {
case SPRING_CLOUD_REGISTRY_PROPERTY_VALUE:
registry = new SpringCloudRegistry(url, discoveryClient,
dubboServiceMetadataRepository, dubboMetadataConfigServiceProxy,
jsonUtils, dubboGenericServiceFactory, applicationContext);
break;
default:
registry = new DubboCloudRegistry(url, discoveryClient,
dubboServiceMetadataRepository, dubboMetadataConfigServiceProxy,
jsonUtils, dubboGenericServiceFactory, applicationContext);
break;
}
return registry;
}
}

@ -16,14 +16,14 @@
package com.alibaba.cloud.dubbo.registry.event;
import java.util.Collection;
import java.util.List;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.ApplicationEventMulticaster;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import static java.util.Collections.unmodifiableCollection;
import static java.util.Collections.unmodifiableList;
/**
* An event raised after the {@link ServiceInstance instances} of one service has been
@ -35,7 +35,7 @@ public class ServiceInstancesChangedEvent extends ApplicationEvent {
private final String serviceName;
private final Collection<ServiceInstance> serviceInstances;
private final List<ServiceInstance> serviceInstances;
/**
* Current event has been processed or not. Typically, Spring Event was based on sync
@ -51,10 +51,10 @@ public class ServiceInstancesChangedEvent extends ApplicationEvent {
* @throws IllegalArgumentException if source is null.
*/
public ServiceInstancesChangedEvent(String serviceName,
Collection<ServiceInstance> serviceInstances) {
List<ServiceInstance> serviceInstances) {
super(serviceName);
this.serviceName = serviceName;
this.serviceInstances = unmodifiableCollection(serviceInstances);
this.serviceInstances = unmodifiableList(serviceInstances);
}
/**
@ -67,7 +67,7 @@ public class ServiceInstancesChangedEvent extends ApplicationEvent {
/**
* @return all {@link ServiceInstance service instances}.
*/
public Collection<ServiceInstance> getServiceInstances() {
public List<ServiceInstance> getServiceInstances() {
return serviceInstances;
}

@ -30,11 +30,13 @@ import javax.annotation.PreDestroy;
import com.alibaba.cloud.dubbo.metadata.DubboRestServiceMetadata;
import com.alibaba.cloud.dubbo.metadata.ServiceRestMetadata;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.spring.ReferenceBean;
import org.apache.dubbo.rpc.service.GenericService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -95,10 +97,9 @@ public class DubboGenericServiceFactory {
private ReferenceBean<GenericService> build(String interfaceName, String version,
String group, Map<String, Object> dubboTranslatedAttributes) {
Integer key = Objects.hash(interfaceName, version, group,
dubboTranslatedAttributes);
String key = createKey(interfaceName, version, group, dubboTranslatedAttributes);
return cache.computeIfAbsent(group + key, k -> {
return cache.computeIfAbsent(key, k -> {
ReferenceBean<GenericService> referenceBean = new ReferenceBean<>();
referenceBean.setGeneric(true);
referenceBean.setInterface(interfaceName);
@ -110,6 +111,12 @@ public class DubboGenericServiceFactory {
});
}
private String createKey(String interfaceName, String version, String group,
Map<String, Object> dubboTranslatedAttributes) {
return group + "#"
+ Objects.hash(interfaceName, version, group, dubboTranslatedAttributes);
}
private void bindReferenceBean(ReferenceBean<GenericService> referenceBean,
Map<String, Object> dubboTranslatedAttributes) {
DataBinder dataBinder = new DataBinder(referenceBean);
@ -155,7 +162,7 @@ public class DubboGenericServiceFactory {
cache.clear();
}
public synchronized void destroy(String serviceName) {
public void destroy(String serviceName) {
Set<String> removeGroups = new HashSet<>(cache.keySet());
for (String key : removeGroups) {
if (key.contains(serviceName)) {

@ -16,13 +16,28 @@
package com.alibaba.cloud.dubbo.service;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import com.alibaba.cloud.dubbo.metadata.repository.DubboServiceMetadataRepository;
import com.alibaba.cloud.dubbo.metadata.repository.ServiceInstanceSelector;
import com.alibaba.cloud.dubbo.util.DubboMetadataUtils;
import org.apache.dubbo.common.URL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import static java.lang.reflect.Proxy.newProxyInstance;
import static org.apache.dubbo.common.constants.CommonConstants.APPLICATION_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
/**
* The proxy of {@link DubboMetadataService}.
@ -31,34 +46,111 @@ import static java.lang.reflect.Proxy.newProxyInstance;
*/
public class DubboMetadataServiceProxy implements BeanClassLoaderAware, DisposableBean {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final DubboGenericServiceFactory dubboGenericServiceFactory;
private final DubboMetadataUtils dubboMetadataUtils;
private final ServiceInstanceSelector serviceInstanceSelector;
private final DiscoveryClient discoveryClient;
private final Map<String, DubboMetadataService> dubboMetadataServiceCache = new ConcurrentHashMap<>();
private ClassLoader classLoader;
public DubboMetadataServiceProxy(
DubboGenericServiceFactory dubboGenericServiceFactory) {
DubboGenericServiceFactory dubboGenericServiceFactory,
DubboServiceMetadataRepository dubboServiceMetadataRepository,
DubboMetadataUtils dubboMetadataUtils,
ServiceInstanceSelector serviceInstanceSelector,
DiscoveryClient discoveryClient) {
this.dubboGenericServiceFactory = dubboGenericServiceFactory;
this.dubboMetadataUtils = dubboMetadataUtils;
this.serviceInstanceSelector = serviceInstanceSelector;
this.discoveryClient = discoveryClient;
}
/**
* Initializes {@link DubboMetadataService}'s Proxy.
* Remove {@link DubboMetadataService}'s Proxy by service name.
* @param serviceName the service name
* @param version the service version
*/
public void removeProxy(String serviceName) {
dubboMetadataServiceCache.remove(serviceName);
dubboGenericServiceFactory.destroy(serviceName);
}
/**
* Get the proxy of {@link DubboMetadataService} if possible
* @param serviceInstances the instances of {@link DubboMetadataService}
* @return <code>null</code> if initialization can't be done
*/
public DubboMetadataService getProxy(List<ServiceInstance> serviceInstances) {
DubboMetadataService dubboMetadataService = null;
// attempt to get the proxy of DubboMetadataService in maximum times
int attempts = serviceInstances.size();
for (int i = 0; i < attempts; i++) {
Optional<ServiceInstance> serviceInstance = select(serviceInstances);
if (serviceInstance.isPresent()) {
List<URL> dubboMetadataServiceURLs = getDubboMetadataServiceURLs(
serviceInstance.get());
for (URL dubboMetadataServiceURL : dubboMetadataServiceURLs) {
dubboMetadataService = createProxyIfAbsent(dubboMetadataServiceURL);
if (dubboMetadataService != null) {
return dubboMetadataService;
}
}
}
}
return dubboMetadataService;
}
/**
* Is the {@link DubboMetadataService}'s Proxy initialized or not
* @param serviceName the service name
* @return <code>true</code> if initialized , or return <code>false</code>
*/
public boolean isInitialized(String serviceName) {
return dubboMetadataServiceCache.containsKey(serviceName);
}
/**
* Create a {@link DubboMetadataService}'s Proxy If abstract.
* @param dubboMetadataServiceURL the {@link URL} of {@link DubboMetadataService}
* @return a {@link DubboMetadataService} proxy
*/
public DubboMetadataService initProxy(String serviceName, String version) {
return dubboMetadataServiceCache.computeIfAbsent(serviceName,
name -> newProxy(name, version));
private DubboMetadataService createProxyIfAbsent(URL dubboMetadataServiceURL) {
String serviceName = dubboMetadataServiceURL.getParameter(APPLICATION_KEY);
String version = dubboMetadataServiceURL.getParameter(VERSION_KEY);
// Initialize DubboMetadataService with right version
return createProxyIfAbsent(serviceName, version);
}
/**
* Remove {@link DubboMetadataService}'s Proxy by service name.
* Initializes {@link DubboMetadataService}'s Proxy.
* @param serviceName the service name
* @param version the service version
* @return a {@link DubboMetadataService} proxy
*/
public void removeProxy(String serviceName) {
dubboMetadataServiceCache.remove(serviceName);
private DubboMetadataService createProxyIfAbsent(String serviceName, String version) {
return dubboMetadataServiceCache.computeIfAbsent(serviceName,
name -> createProxy(name, version));
}
private Optional<ServiceInstance> select(List<ServiceInstance> serviceInstances) {
return serviceInstanceSelector.select(serviceInstances);
}
private List<URL> getDubboMetadataServiceURLs(ServiceInstance serviceInstance) {
return dubboMetadataUtils.getDubboMetadataServiceURLs(serviceInstance);
}
/**
@ -68,7 +160,12 @@ public class DubboMetadataServiceProxy implements BeanClassLoaderAware, Disposab
* @return a {@link DubboMetadataService} proxy
*/
public DubboMetadataService getProxy(String serviceName) {
return dubboMetadataServiceCache.get(serviceName);
return dubboMetadataServiceCache.getOrDefault(serviceName,
getProxy0(serviceName));
}
private DubboMetadataService getProxy0(String serviceName) {
return getProxy(discoveryClient.getInstances(serviceName));
}
@Override
@ -88,7 +185,14 @@ public class DubboMetadataServiceProxy implements BeanClassLoaderAware, Disposab
* @param version the service version
* @return a {@link DubboMetadataService} proxy
*/
protected DubboMetadataService newProxy(String serviceName, String version) {
protected DubboMetadataService createProxy(String serviceName, String version) {
if (logger.isInfoEnabled()) {
logger.info(
"The metadata of Dubbo service[name : {}] is about to be initialized",
serviceName);
}
return (DubboMetadataService) newProxyInstance(classLoader,
new Class[] { DubboMetadataService.class },
new DubboMetadataServiceInvocationHandler(serviceName, version,

@ -28,4 +28,19 @@ public interface DubboCloudConstants {
*/
String CONFIG_PROPERTY_PREFIX = "dubbo.cloud";
/**
* The property name of Registry type
*/
String REGISTRY_TYPE_PROPERTY_NAME = CONFIG_PROPERTY_PREFIX + ".registry-type";
/**
* The property value of Spring Cloud Registry
*/
String SPRING_CLOUD_REGISTRY_PROPERTY_VALUE = "spring-cloud";
/**
* The property value of Dubbo Cloud Registry
*/
String DUBBO_CLOUD_REGISTRY_PROPERTY_VALUE = "dubbo-cloud";
}

@ -0,0 +1,118 @@
/*
* 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 com.alibaba.cloud.dubbo.util;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import com.alibaba.cloud.dubbo.service.DubboMetadataService;
import org.apache.dubbo.common.URL;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.core.env.Environment;
import static java.lang.String.format;
import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URLS_PROPERTY_NAME;
import static org.springframework.util.StringUtils.hasText;
/**
* The utilities class of Dubbo Metadata
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class DubboMetadataUtils {
/**
* The {@link String#format(String, Object...) pattern} of dubbo protocols port.
*/
public static final String DUBBO_PROTOCOLS_PORT_PROPERTY_NAME_PATTERN = "dubbo.protocols.%s.port";
private final JSONUtils jsonUtils;
private final Environment environment;
private final String currentApplicationName;
public DubboMetadataUtils(JSONUtils jsonUtils, Environment environment) {
this.jsonUtils = jsonUtils;
this.environment = environment;
this.currentApplicationName = environment.getProperty("spring.application.name",
environment.getProperty("dubbo.application.name", "application"));
}
/**
* Get the current application name
* @return non-null
*/
public String getCurrentApplicationName() {
return currentApplicationName;
}
/**
* Get the {@link URL urls} that {@link DubboMetadataService} exported by the
* specified {@link ServiceInstance}.
* @param serviceInstance {@link ServiceInstance}
* @return the mutable {@link URL urls}
*/
public List<URL> getDubboMetadataServiceURLs(ServiceInstance serviceInstance) {
Map<String, String> metadata = serviceInstance.getMetadata();
String dubboURLsJSON = metadata.get(METADATA_SERVICE_URLS_PROPERTY_NAME);
return jsonUtils.toURLs(dubboURLsJSON);
}
/**
* Get the {@link URL urls} that {@link DubboMetadataService} exported by the
* specified {@link ServiceInstance ServiceInstances}.
* @param serviceInstances the list of {@link ServiceInstance ServiceInstances}
* @param serviceInterface the interface name of Dubbo service
* @param version the version of Dubbo service
* @param protocol the protocol that Dubbo Service exports
* @return the mutable {@link URL urls}
*/
public List<URL> getDubboMetadataServiceURLs(List<ServiceInstance> serviceInstances,
String serviceInterface, String version, String protocol) {
return serviceInstances.stream().map(this::getDubboMetadataServiceURLs)
.flatMap(List::stream)
.filter(url -> protocol == null
|| Objects.equals(protocol, url.getProtocol()))
.filter(url -> Objects.equals(serviceInterface,
url.getServiceInterface()))
.filter(url -> Objects.equals(version, url.getParameter(VERSION_KEY)))
.collect(Collectors.toList());
}
/**
* Get the property name of Dubbo Protocol.
* @param protocol Dubbo Protocol
* @return non-null
*/
public String getDubboProtocolPropertyName(String protocol) {
return format(DUBBO_PROTOCOLS_PORT_PROPERTY_NAME_PATTERN, protocol);
}
public Integer getDubboProtocolPort(ServiceInstance serviceInstance,
String protocol) {
String protocolProperty = getDubboProtocolPropertyName(protocol);
Map<String, String> metadata = serviceInstance.getMetadata();
String protocolPort = metadata.get(protocolProperty);
return hasText(protocolPort) ? Integer.valueOf(protocolPort) : null;
}
}
Loading…
Cancel
Save