fix bug 1529

pull/1691/head
niuzhiweimr 5 years ago
parent 96b5db65b7
commit 17a9722522

@ -1,9 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<parent>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-starters</artifactId>
<version>${revision}</version>

@ -17,18 +17,18 @@
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;
@ -44,7 +44,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 +52,8 @@ import org.springframework.util.CollectionUtils;
*/
@Configuration
@Import({ DubboServiceMetadataRepository.class, IntrospectiveDubboMetadataService.class,
DubboMetadataServiceExporter.class, JSONUtils.class })
DubboMetadataServiceExporter.class, JSONUtils.class,
DubboMetadataServiceProxy.class, DubboMetadataUtils.class })
public class DubboMetadataAutoConfiguration {
@Autowired
@ -73,9 +73,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 +83,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;
@ -134,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,
@ -153,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()) {

@ -22,6 +22,8 @@ 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;
@ -31,7 +33,7 @@ import static org.springframework.util.StringUtils.trimAllWhitespace;
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@ConfigurationProperties(prefix = "dubbo.cloud")
@ConfigurationProperties(prefix = CONFIG_PROPERTY_PREFIX)
public class DubboCloudProperties {
/**
@ -47,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;
}
@ -79,4 +83,12 @@ public class DubboCloudProperties {
return Collections.unmodifiableSet(subscribedServices);
}
public String getRegistryType() {
return registryType;
}
public void setRegistryType(String registryType) {
this.registryType = registryType;
}
}

@ -84,7 +84,7 @@ public class DubboNonWebApplicationEnvironmentPostProcessor
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment,
SpringApplication application) {
SpringApplication application) {
WebApplicationType webApplicationType = application.getWebApplicationType();
if (!WebApplicationType.NONE.equals(webApplicationType)) { // Just works in
@ -117,7 +117,7 @@ public class DubboNonWebApplicationEnvironmentPostProcessor
* @param defaultProperties defaultProperties
*/
private void resetServerPort(ConfigurableEnvironment environment,
Map<String, Object> defaultProperties) {
Map<String, Object> defaultProperties) {
String serverPort = environment.getProperty(SERVER_PORT_PROPERTY_NAME,
environment.getProperty(PORT_PROPERTY_NAME));
@ -181,7 +181,7 @@ public class DubboNonWebApplicationEnvironmentPostProcessor
}
private void setServerPort(ConfigurableEnvironment environment, String serverPort,
Map<String, Object> defaultProperties) {
Map<String, Object> defaultProperties) {
if (serverPort == null) {
return;
}
@ -196,7 +196,7 @@ public class DubboNonWebApplicationEnvironmentPostProcessor
* @param map Default Dubbo Properties
*/
private void addOrReplace(MutablePropertySources propertySources,
Map<String, Object> map) {
Map<String, Object> map) {
MapPropertySource target = null;
if (propertySources.contains(PROPERTY_SOURCE_NAME)) {
PropertySource<?> source = propertySources.get(PROPERTY_SOURCE_NAME);

@ -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,6 +36,7 @@ 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;
@ -61,15 +60,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 +84,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 +108,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 +117,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 +161,7 @@ public class DubboServiceMetadataRepository
private DiscoveryClient discoveryClient;
@Autowired
private MetadataServiceInstanceSelector metadataServiceInstanceSelector;
private ServiceInstanceSelector serviceInstanceSelector;
@Autowired
private JSONUtils jsonUtils;
@ -180,6 +169,9 @@ public class DubboServiceMetadataRepository
@Autowired
private InetUtils inetUtils;
@Autowired
private DubboMetadataUtils dubboMetadataUtils;
@Value("${spring.application.name}")
private String currentApplicationName;
@ -190,7 +182,7 @@ public class DubboServiceMetadataRepository
// //
private static <K, V> Map<K, V> getMap(Map<String, Map<K, V>> repository,
String key) {
String key) {
return getOrDefault(repository, key, newHashMap());
}
@ -275,28 +267,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 +282,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);
}
}
}
@ -343,10 +311,9 @@ public class DubboServiceMetadataRepository
}
private void addDubboMetadataServiceURLsMetadata(Map<String, String> metadata,
List<URL> dubboMetadataServiceURLs) {
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 +326,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,27 +347,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
@ -459,28 +396,17 @@ 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) {
return dubboMetadataUtils.getDubboProtocolPort(serviceInstance, 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;
private String getDubboProtocolPropertyName(String protocol) {
return dubboMetadataUtils.getDubboProtocolPropertyName(protocol);
}
public List<URL> getExportedURLs(String serviceInterface, String group,
String version) {
String version) {
String serviceKey = URL.buildKey(serviceInterface, group, version);
return allExportedURLs.getOrDefault(serviceKey, Collections.emptyList());
}
@ -492,6 +418,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;
}
@ -537,7 +468,7 @@ public class DubboServiceMetadataRepository
* @return {@link DubboRestServiceMetadata} if matched, or <code>null</code>
*/
public DubboRestServiceMetadata get(String serviceName,
RequestMetadata requestMetadata) {
RequestMetadata requestMetadata) {
return match(dubboRestServiceMetadataRepository, serviceName, requestMetadata);
}
@ -554,7 +485,7 @@ public class DubboServiceMetadataRepository
}
private <T> T match(Map<String, Map<RequestMetadataMatcher, T>> repository,
String serviceName, RequestMetadata requestMetadata) {
String serviceName, RequestMetadata requestMetadata) {
Map<RequestMetadataMatcher, T> map = repository.get(serviceName);
@ -600,8 +531,7 @@ public class DubboServiceMetadataRepository
Set<ServiceRestMetadata> metadata = emptySet();
DubboMetadataService dubboMetadataService = dubboMetadataConfigServiceProxy
.getProxy(serviceName);
DubboMetadataService dubboMetadataService = getProxy(serviceName);
if (dubboMetadataService != null) {
try {
@ -626,67 +556,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;
@Override
public void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
private void initSubscribedDubboMetadataServiceURL(URL dubboMetadataServiceURL) {
// add subscriptions
String serviceKey = dubboMetadataServiceURL.getServiceKey();
subscribedDubboMetadataServiceURLs.add(serviceKey, dubboMetadataServiceURL);
}
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);
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);
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;
}
@Override
public void setApplicationEventPublisher(
ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
}

@ -0,0 +1,44 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed 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
*
* https://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.
* @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 {
/**
@ -98,10 +99,10 @@ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
private final ConfigurableApplicationContext applicationContext;
public AbstractSpringCloudRegistry(URL url, DiscoveryClient discoveryClient,
DubboServiceMetadataRepository dubboServiceMetadataRepository,
DubboMetadataServiceProxy dubboMetadataConfigServiceProxy,
JSONUtils jsonUtils, DubboGenericServiceFactory dubboGenericServiceFactory,
ConfigurableApplicationContext applicationContext) {
DubboServiceMetadataRepository dubboServiceMetadataRepository,
DubboMetadataServiceProxy dubboMetadataConfigServiceProxy,
JSONUtils jsonUtils, DubboGenericServiceFactory dubboGenericServiceFactory,
ConfigurableApplicationContext applicationContext) {
super(url);
this.servicesLookupInterval = url
.getParameter(SERVICES_LOOKUP_INTERVAL_PARAM_NAME, 60L);
@ -190,7 +191,7 @@ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
* @param listener {@link NotifyListener}
*/
private void registerServiceInstancesChangedEventListener(URL url,
NotifyListener listener) {
NotifyListener listener) {
String listenerId = generateId(url);
if (registerListeners.add(listenerId)) {
applicationContext.addApplicationListener(
@ -217,8 +218,8 @@ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
}
protected void subscribeDubboServiceURL(URL url, NotifyListener listener,
String serviceName,
Function<String, Collection<ServiceInstance>> serviceInstancesFunction) {
String serviceName,
Function<String, Collection<ServiceInstance>> serviceInstancesFunction) {
if (logger.isInfoEnabled()) {
logger.info(
@ -355,7 +356,7 @@ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
}
private List<URL> getExportedURLs(DubboMetadataService dubboMetadataService,
URL url) {
URL url) {
String serviceInterface = url.getServiceInterface();
String group = url.getParameter(GROUP_KEY);
String version = url.getParameter(VERSION_KEY);
@ -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,511 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed 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
*
* https://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.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.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,
getServiceName(subscribedURL));
// Sync subscription
if (containsProviderCategory(subscribedURL)) {
registerServiceInstancesChangedListener(subscribedURL, event -> {
String sourceServiceName = event.getServiceName();
String serviceName = getServiceName(subscribedURL);
if (Objects.equals(sourceServiceName, serviceName)) {
subscribeDubboMetadataServiceURLs(subscribedURL, listener,
sourceServiceName);
}
});
}
}
private String getServiceName(URL subscribedURL) {
return subscribedURL.getParameter(GROUP_KEY);
}
private void subscribeDubboMetadataServiceURLs(URL subscribedURL,
NotifyListener listener, String serviceName) {
String serviceInterface = subscribedURL.getServiceInterface();
String version = subscribedURL.getParameter(VERSION_KEY);
String protocol = subscribedURL.getParameter(PROTOCOL_KEY);
List<ServiceInstance> serviceInstances = getServiceInstances(serviceName);
List<URL> urls = dubboMetadataUtils.getDubboMetadataServiceURLs(serviceInstances,
serviceInterface, version, protocol);
notifyAllSubscribedURLs(subscribedURL, urls, listener);
}
// 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,6 +16,7 @@
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;
@ -23,11 +24,12 @@ 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;
import org.apache.dubbo.registry.support.AbstractRegistryFactory;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.ConfigurableApplicationContext;
import static java.lang.System.getProperty;
import static com.alibaba.cloud.dubbo.util.DubboCloudConstants.SPRING_CLOUD_REGISTRY_PROPERTY_VALUE;
/**
* Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose
@ -37,7 +39,7 @@ import static java.lang.System.getProperty;
* @see RegistryFactory
* @see SpringCloudRegistry
*/
public class SpringCloudRegistryFactory implements RegistryFactory {
public class SpringCloudRegistryFactory extends AbstractRegistryFactory {
/**
* Spring Cloud Protocol.
@ -49,10 +51,6 @@ public class SpringCloudRegistryFactory implements RegistryFactory {
*/
public static String ADDRESS = "localhost";
private static String SERVICES_LOOKUP_SCHEDULER_THREAD_NAME_PREFIX = getProperty(
"dubbo.services.lookup.scheduler.thread.name.prefix ",
"dubbo-services-lookup-");
private static ConfigurableApplicationContext applicationContext;
private DiscoveryClient discoveryClient;
@ -65,8 +63,6 @@ public class SpringCloudRegistryFactory implements RegistryFactory {
private DubboGenericServiceFactory dubboGenericServiceFactory;
private volatile boolean initialized = false;
public SpringCloudRegistryFactory() {
}
@ -76,9 +72,6 @@ public class SpringCloudRegistryFactory implements RegistryFactory {
}
protected void init() {
if (initialized || applicationContext == null) {
return;
}
this.discoveryClient = applicationContext.getBean(DiscoveryClient.class);
this.dubboServiceMetadataRepository = applicationContext
.getBean(DubboServiceMetadataRepository.class);
@ -90,11 +83,28 @@ public class SpringCloudRegistryFactory implements RegistryFactory {
}
@Override
public Registry getRegistry(URL url) {
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;
}
}

@ -95,10 +95,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 +109,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);
@ -127,7 +132,7 @@ public class DubboGenericServiceFactory {
// Trim all whitespace
String content = StringUtils.trimAllWhitespace(text);
if (!StringUtils.hasText(content)) { // No content , ignore
// directly
// directly
return;
}
// replace "=" to ","
@ -155,7 +160,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,26 @@
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 +44,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 +158,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 +183,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,

@ -0,0 +1,51 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed 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
*
* https://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;
/**
* The constants for Dubbo Spring Cloud.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public final class DubboCloudConstants {
/**
* The property prefix of Configuration.
*/
public static final String CONFIG_PROPERTY_PREFIX = "dubbo.cloud";
/**
* The property name of Registry type.
*/
public static final String REGISTRY_TYPE_PROPERTY_NAME = CONFIG_PROPERTY_PREFIX
+ ".registry-type";
/**
* The property value of Spring Cloud Registry.
*/
public static final String SPRING_CLOUD_REGISTRY_PROPERTY_VALUE = "spring-cloud";
/**
* The property value of Dubbo Cloud Registry.
*/
public static final String DUBBO_CLOUD_REGISTRY_PROPERTY_VALUE = "dubbo-cloud";
private DubboCloudConstants() {
throw new AssertionError("Must not instantiate constant utility class");
}
}

@ -0,0 +1,118 @@
/*
* Copyright 2013-2018 the original author or authors.
*
* Licensed 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
*
* https://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;
}
}

@ -1,7 +1,6 @@
# Dubbo Endpoints Default Properties is loaded by @PropertySource with low order,
# Set enabled for Dubbo Endpoints
management.endpoint.dubborestmetadata.enabled = true
management.endpoint.dubborestmetadata.enabled=true
# "management.endpoints.web.base-path" should not be configured in this file
# Re-defines path-mapping of Dubbo Web Endpoints
management.endpoints.web.path-mapping.dubborestmetadata = dubbo/rest/metadata
management.endpoints.web.path-mapping.dubborestmetadata=dubbo/rest/metadata

@ -6,12 +6,9 @@ com.alibaba.cloud.dubbo.autoconfigure.DubboServiceRegistrationNonWebApplicationA
com.alibaba.cloud.dubbo.autoconfigure.DubboLoadBalancedRestTemplateAutoConfiguration,\
com.alibaba.cloud.dubbo.autoconfigure.DubboServiceAutoConfiguration,\
com.alibaba.cloud.dubbo.autoconfigure.DubboServiceDiscoveryAutoConfiguration
org.springframework.boot.actuate.autoconfigure.web.ManagementContextConfiguration=\
com.alibaba.cloud.dubbo.actuate.DubboMetadataEndpointAutoConfiguration
org.springframework.context.ApplicationContextInitializer=\
com.alibaba.cloud.dubbo.context.DubboServiceRegistrationApplicationContextInitializer
org.springframework.boot.env.EnvironmentPostProcessor=\
com.alibaba.cloud.dubbo.env.DubboNonWebApplicationEnvironmentPostProcessor
Loading…
Cancel
Save