Merge pull request #562 from mercyblitz/master

[Refactor] add Introspective DubboMetadataService for Dubbo Spring Cloud
pull/579/head
Mercy Ma 6 years ago committed by GitHub
commit d535c6f74f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -32,7 +32,7 @@ import org.springframework.cloud.alibaba.dubbo.metadata.resolver.MetadataResolve
import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceFactory;
import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataServiceExporter;
import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataServiceProxy;
import org.springframework.cloud.alibaba.dubbo.service.PublishingDubboMetadataService;
import org.springframework.cloud.alibaba.dubbo.service.IntrospectiveDubboMetadataService;
import org.springframework.cloud.alibaba.dubbo.util.JSONUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -50,13 +50,13 @@ import java.util.function.Supplier;
*/
@Configuration
@Import({DubboServiceMetadataRepository.class,
PublishingDubboMetadataService.class,
IntrospectiveDubboMetadataService.class,
DubboMetadataServiceExporter.class,
JSONUtils.class})
public class DubboMetadataAutoConfiguration {
@Autowired
private PublishingDubboMetadataService dubboMetadataService;
private ObjectProvider<DubboServiceMetadataRepository> dubboServiceMetadataRepository;
@Autowired
private MetadataResolver metadataResolver;
@ -87,7 +87,6 @@ public class DubboMetadataAutoConfiguration {
public void onServiceBeanExported(ServiceBeanExportedEvent event) {
ServiceBean serviceBean = event.getServiceBean();
publishServiceRestMetadata(serviceBean);
exportDubboMetadataConfigService();
}
@EventListener(ApplicationFailedEvent.class)
@ -97,15 +96,11 @@ public class DubboMetadataAutoConfiguration {
@EventListener(ContextClosedEvent.class)
public void onContextClosed() {
dubboMetadataConfigServiceExporter.unexport();
unExportDubboMetadataConfigService();
}
private void publishServiceRestMetadata(ServiceBean serviceBean) {
dubboMetadataService.publishServiceRestMetadata(metadataResolver.resolveServiceRestMetadata(serviceBean));
}
private void exportDubboMetadataConfigService() {
dubboMetadataConfigServiceExporter.export();
dubboServiceMetadataRepository.getIfAvailable().publishServiceRestMetadata(metadataResolver.resolveServiceRestMetadata(serviceBean));
}
private void unExportDubboMetadataConfigService() {

@ -16,7 +16,6 @@
*/
package org.springframework.cloud.alibaba.dubbo.autoconfigure;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.config.spring.ServiceBean;
import com.ecwid.consul.v1.agent.model.NewService;
@ -35,7 +34,6 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
import org.springframework.cloud.alibaba.dubbo.registry.DubboServiceRegistrationEventPublishingAspect;
import org.springframework.cloud.alibaba.dubbo.registry.event.ServiceInstancePreRegisteredEvent;
import org.springframework.cloud.alibaba.dubbo.util.JSONUtils;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.consul.serviceregistry.ConsulRegistration;
@ -46,17 +44,13 @@ import org.springframework.context.SmartLifecycle;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.event.EventListener;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.CONSUL_AUTO_CONFIGURATION_CLASS_NAME;
import static org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.EUREKA_AUTO_CONFIGURATION_CLASS_NAME;
import static org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository.DUBBO_URLS_METADATA_PROPERTY_NAME;
import static org.springframework.util.ObjectUtils.isEmpty;
/**
@ -93,13 +87,10 @@ public class DubboServiceRegistrationAutoConfiguration {
@Autowired
private DubboServiceMetadataRepository dubboServiceMetadataRepository;
@Autowired
private JSONUtils jsonUtils;
@EventListener(ServiceInstancePreRegisteredEvent.class)
public void onServiceInstancePreRegistered(ServiceInstancePreRegisteredEvent event) {
Registration registration = event.getSource();
attachURLsIntoMetadata(registration);
attachDubboMetadataServiceMetadata(registration);
}
@Configuration
@ -115,7 +106,7 @@ public class DubboServiceRegistrationAutoConfiguration {
Registration registration = event.getSource();
EurekaRegistration eurekaRegistration = EurekaRegistration.class.cast(registration);
InstanceInfo instanceInfo = eurekaRegistration.getApplicationInfoManager().getInfo();
attachURLsIntoMetadata(instanceInfo.getMetadata());
attachDubboMetadataServiceMetadata(instanceInfo.getMetadata());
}
/**
@ -155,39 +146,30 @@ public class DubboServiceRegistrationAutoConfiguration {
private void attachURLsIntoMetadata(ConsulRegistration consulRegistration) {
NewService newService = consulRegistration.getService();
String dubboURLsJson = getDubboURLsJSON();
if (StringUtils.hasText(dubboURLsJson)) {
Map<String, String> serviceMetadata = dubboServiceMetadataRepository.getDubboMetadataServiceMetadata();
if (!isEmpty(serviceMetadata)) {
List<String> tags = newService.getTags();
tags.add(DUBBO_URLS_METADATA_PROPERTY_NAME + "=" + dubboURLsJson);
for (Map.Entry<String, String> entry : serviceMetadata.entrySet()) {
tags.add(entry.getKey() + "=" + entry.getValue());
}
}
}
}
private void attachURLsIntoMetadata(Registration registration) {
private void attachDubboMetadataServiceMetadata(Registration registration) {
if (registration == null) {
return;
}
synchronized (registration) {
Map<String, String> metadata = registration.getMetadata();
attachURLsIntoMetadata(metadata);
}
}
private void attachURLsIntoMetadata(Map<String, String> metadata) {
String dubboURLsJson = getDubboURLsJSON();
if (StringUtils.hasText(dubboURLsJson)) {
metadata.put(DUBBO_URLS_METADATA_PROPERTY_NAME, dubboURLsJson);
attachDubboMetadataServiceMetadata(metadata);
}
}
private String getDubboURLsJSON() {
Collection<URL> urls = dubboServiceMetadataRepository.getRegisteredUrls();
if (CollectionUtils.isEmpty(urls)) {
if (logger.isDebugEnabled()) {
logger.debug("There is no registered URL to attach into metadata.");
}
return null;
private void attachDubboMetadataServiceMetadata(Map<String, String> metadata) {
Map<String, String> serviceMetadata = dubboServiceMetadataRepository.getDubboMetadataServiceMetadata();
if (!isEmpty(serviceMetadata)) {
metadata.putAll(serviceMetadata);
}
return jsonUtils.toJSON(urls.stream().map(URL::toFullString).collect(Collectors.toList()));
}
}

@ -41,7 +41,7 @@ import org.springframework.cloud.zookeeper.serviceregistry.ServiceInstanceRegist
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
import java.util.Collection;
import java.util.List;
import static org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.CONSUL_AUTO_CONFIGURATION_CLASS_NAME;
import static org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration.ZOOKEEPER_AUTO_CONFIGURATION_CLASS_NAME;
@ -97,20 +97,21 @@ public class DubboServiceRegistrationNonWebApplicationAutoConfiguration {
*/
private void setServerPort() {
if (serverPort == null) {
Collection<URL> urls = repository.getRegisteredUrls();
urls.stream()
.filter(url -> REST_PROTOCOL.equalsIgnoreCase(url.getProtocol()))
.findFirst()
.ifPresent(url -> {
for (List<URL> urls : repository.getAllExportedUrls().values()) {
urls.stream()
.filter(url -> REST_PROTOCOL.equalsIgnoreCase(url.getProtocol()))
.findFirst()
.ifPresent(url -> {
serverPort = url.getPort();
});
// If REST protocol is not present, use any applied port.
if (serverPort == null) {
urls.stream()
.findAny().ifPresent(url -> {
serverPort = url.getPort();
});
// If REST protocol is not present, use any applied port.
if (serverPort == null) {
urls.stream()
.findAny().ifPresent(url -> {
serverPort = url.getPort();
});
}
}
}
}

@ -25,6 +25,7 @@ import java.util.Iterator;
import java.util.function.Supplier;
import static org.apache.dubbo.common.Constants.DEFAULT_PROTOCOL;
import static org.springframework.util.CollectionUtils.isEmpty;
/**
* Dubbo's {@link ProtocolConfig} {@link Supplier}
@ -43,23 +44,26 @@ public class DubboProtocolConfigSupplier implements Supplier<ProtocolConfig> {
public ProtocolConfig get() {
ProtocolConfig protocolConfig = null;
Collection<ProtocolConfig> protocols = this.protocols.getIfAvailable();
for (ProtocolConfig protocol : protocols) {
String protocolName = protocol.getName();
if (DEFAULT_PROTOCOL.equals(protocolName)) {
protocolConfig = protocol;
break;
if (!isEmpty(protocols)) {
for (ProtocolConfig protocol : protocols) {
String protocolName = protocol.getName();
if (DEFAULT_PROTOCOL.equals(protocolName)) {
protocolConfig = protocol;
break;
}
}
}
if (protocolConfig == null) { // If The ProtocolConfig bean named "dubbo" is absent, take first one of them
Iterator<ProtocolConfig> iterator = protocols.iterator();
protocolConfig = iterator.hasNext() ? iterator.next() : null;
if (protocolConfig == null) { // If The ProtocolConfig bean named "dubbo" is absent, take first one of them
Iterator<ProtocolConfig> iterator = protocols.iterator();
protocolConfig = iterator.hasNext() ? iterator.next() : null;
}
}
if (protocolConfig == null) {
protocolConfig = new ProtocolConfig();
protocolConfig.setName(DEFAULT_PROTOCOL);
protocolConfig.setPort(20880);
protocolConfig.setPort(-1);
}
return protocolConfig;

@ -30,6 +30,7 @@ import org.springframework.cloud.alibaba.dubbo.metadata.DubboRestServiceMetadata
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataService;
import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataServiceExporter;
import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataServiceProxy;
import org.springframework.cloud.alibaba.dubbo.util.JSONUtils;
import org.springframework.cloud.client.ServiceInstance;
@ -37,12 +38,12 @@ import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.HttpRequest;
import org.springframework.stereotype.Repository;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import javax.annotation.PostConstruct;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@ -51,10 +52,19 @@ import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
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.APPLICATION_KEY;
import static org.apache.dubbo.common.Constants.VERSION_KEY;
import static org.springframework.cloud.alibaba.dubbo.env.DubboCloudProperties.ALL_DUBBO_SERVICES;
import static org.springframework.cloud.alibaba.dubbo.http.DefaultHttpRequest.builder;
import static org.springframework.util.CollectionUtils.isEmpty;
import static org.springframework.util.StringUtils.hasText;
/**
* Dubbo Service Metadata {@link Repository}
@ -65,17 +75,56 @@ import static org.springframework.util.CollectionUtils.isEmpty;
public class DubboServiceMetadataRepository {
/**
* The property name of Dubbo {@link URL URLs} metadata
* The prefix of {@link DubboMetadataService} : "dubbo.metadata-service."
*/
public static final String DUBBO_URLS_METADATA_PROPERTY_NAME = "dubbo.urls";
public static final String DUBBO_METADATA_SERVICE_PREFIX = "dubbo.metadata-service.";
/**
* 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";
/**
* 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 Logger logger = LoggerFactory.getLogger(getClass());
private final ObjectMapper objectMapper = new ObjectMapper();
private final Set<URL> registeredURLs = new LinkedHashSet<>();
// =================================== Registration =================================== //
/**
* 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 URLs}
*/
private final MultiValueMap<String, URL> allExportedURLs = new LinkedMultiValueMap<>();
// ==================================================================================== //
// =================================== Subscription =================================== //
private Set<String> subscribedServices;
/**
* 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<>();
// ==================================================================================== //
private final Map<String, String> dubboServiceKeysRepository = new HashMap<>();
// =================================== REST Metadata ================================== //
/**
* A Map to store REST metadata temporary, its' key is the special service name for a Dubbo service,
* the value is a JSON content of JAX-RS or Spring MVC REST metadata from the annotated methods.
*/
private final Set<ServiceRestMetadata> serviceRestMetadata = new LinkedHashSet<>();
/**
* Key is application name
@ -83,7 +132,10 @@ public class DubboServiceMetadataRepository {
*/
private Map<String, Map<RequestMetadataMatcher, DubboRestServiceMetadata>> dubboRestServiceMetadataRepository = newHashMap();
private Set<String> subscribedServices;
// ==================================================================================== //
// =================================== Dependencies =================================== //
@Autowired
private DubboCloudProperties dubboCloudProperties;
@ -100,67 +152,179 @@ public class DubboServiceMetadataRepository {
@Value("${spring.application.name}")
private String currentApplicationName;
@Autowired
private DubboMetadataServiceExporter dubboMetadataServiceExporter;
// ==================================================================================== //
@PostConstruct
public void init() {
// Keep the order in following invocations
initSubscribedServices();
initDubboServiceKeysRepository();
retainAvailableSubscribedServices();
initSubscribedDubboMetadataServices();
initDubboRestServiceMetadataRepository();
}
/**
* The specified service is subscribe or not
* Get the metadata {@link Map} of {@link DubboMetadataService}
*
* @param serviceName the service name
* @return
* @return non-null read-only {@link Map}
*/
public boolean isSubscribedService(String serviceName) {
return subscribedServices.contains(serviceName);
public Map<String, String> getDubboMetadataServiceMetadata() {
List<URL> dubboMetadataServiceURLs = dubboMetadataServiceExporter.export();
// remove the exported URLs of DubboMetadataService
removeDubboMetadataServiceURLs(dubboMetadataServiceURLs);
Map<String, String> metadata = newHashMap();
addDubboMetadataServiceURLsMetadata(metadata, dubboMetadataServiceURLs);
addDubboProtocolsPortMetadata(metadata);
return Collections.unmodifiableMap(metadata);
}
private void removeDubboMetadataServiceURLs(List<URL> dubboMetadataServiceURLs) {
dubboMetadataServiceURLs.forEach(this::unexportURL);
}
private void addDubboMetadataServiceURLsMetadata(Map<String, String> metadata, List<URL> dubboMetadataServiceURLs) {
String dubboMetadataServiceURLsJSON = jsonUtils.toJSON(dubboMetadataServiceURLs);
metadata.put(DUBBO_METADATA_SERVICE_URLS_PROPERTY_NAME, dubboMetadataServiceURLsJSON);
}
private void addDubboProtocolsPortMetadata(Map<String, String> metadata) {
allExportedURLs.values()
.stream()
.flatMap(v -> v.stream())
.forEach(url -> {
String protocol = url.getProtocol();
String propertyName = getDubboProtocolPropertyName(protocol);
String propertyValue = valueOf(url.getPort());
metadata.put(propertyName, propertyValue);
});
}
/**
* Get the service name by the {@link URL#getServiceKey() service key}
* Get the property name of Dubbo Protocol
*
* @param url {@link URL}
* @return the service name if found
* @param protocol Dubbo Protocol
* @return non-null
*/
public String getServiceName(URL url) {
return getServiceName(url.getServiceKey());
public String getDubboProtocolPropertyName(String protocol) {
return format(DUBBO_PROTOCOLS_PORT_PROPERTY_NAME_PATTERN, protocol);
}
/**
* Get the service name by the {@link URL#getServiceKey() service key}
* Publish the {@link Set} of {@link ServiceRestMetadata}
*
* @param serviceKey the {@link URL#getServiceKey() service key}
* @return the service name if found
* @param serviceRestMetadataSet the {@link Set} of {@link ServiceRestMetadata}
*/
public String getServiceName(String serviceKey) {
return dubboServiceKeysRepository.get(serviceKey);
public void publishServiceRestMetadata(Set<ServiceRestMetadata> serviceRestMetadataSet) {
for (ServiceRestMetadata serviceRestMetadata : serviceRestMetadataSet) {
if (!isEmpty(serviceRestMetadata.getMeta())) {
this.serviceRestMetadata.add(serviceRestMetadata);
}
}
}
/**
* Get the {@link Set} of {@link ServiceRestMetadata}
*
* @return non-null read-only {@link Set}
*/
public Set<ServiceRestMetadata> getServiceRestMetadata() {
return unmodifiableSet(serviceRestMetadata);
}
// /**
// * Get The subscribed {@link DubboMetadataService}'s {@link URL URLs}
// *
// * @return non-null read-only {@link List}
// */
// public List<URL> getSubscribedDubboMetadataServiceURLs() {
// return Collections.unmodifiableList(subscribedDubboMetadataServiceURLs);
// }
public List<URL> findSubscribedDubboMetadataServiceURLs(String serviceName, String group, String version,
String protocol) {
String serviceKey = URL.buildKey(serviceName, group, version);
List<URL> 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
* @return
*/
public boolean isSubscribedService(String serviceName) {
return subscribedServices.contains(serviceName);
}
public void registerURL(URL url) {
this.registeredURLs.add(url);
public void exportURL(URL url) {
this.allExportedURLs.add(url.getServiceKey(), url);
}
public void unregisterURL(URL url) {
this.registeredURLs.remove(url);
public void unexportURL(URL url) {
String key = url.getServiceKey();
List<URL> urls = allExportedURLs.get(key);
urls.remove(url);
this.allExportedURLs.addAll(key, urls);
}
public Collection<URL> getRegisteredUrls() {
return Collections.unmodifiableSet(registeredURLs);
/**
* Get all exported {@link URL urls}.
*
* @return non-null read-only
*/
public Map<String, List<URL>> getAllExportedUrls() {
return unmodifiableMap(allExportedURLs);
}
/**
* Build the {@link URL urls} by the specified {@link ServiceInstance}
* Get all exported {@link URL#getServiceKey() service keys}
*
* @return non-null read-only
*/
public Set<String> getAllServiceKeys() {
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> buildURLs(ServiceInstance serviceInstance) {
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 dubboURLsJSON = metadata.get(DUBBO_URLS_METADATA_PROPERTY_NAME);
List<String> urlValues = jsonUtils.toList(dubboURLsJSON);
return urlValues.stream().map(URL::valueOf).collect(Collectors.toList());
String protocolPort = metadata.get(protocolProperty);
return hasText(protocolPort) ? Integer.valueOf(protocolPort) : null;
}
public List<URL> getExportedURLs(String serviceInterface, String group, String version) {
String serviceKey = URL.buildKey(serviceInterface, group, version);
return allExportedURLs.getOrDefault(serviceKey, Collections.emptyList());
}
/**
@ -212,6 +376,10 @@ public class DubboServiceMetadataRepository {
return match(dubboRestServiceMetadataRepository, serviceName, requestMetadata);
}
public Set<String> getSubscribedServices() {
return Collections.unmodifiableSet(subscribedServices);
}
private <T> T match(Map<String, Map<RequestMetadataMatcher, T>> repository, String serviceName,
RequestMetadata requestMetadata) {
@ -256,18 +424,22 @@ public class DubboServiceMetadataRepository {
}
private Set<ServiceRestMetadata> getServiceRestMetadataSet(String serviceName) {
DubboMetadataService dubboMetadataService = dubboMetadataConfigServiceProxy.newProxy(serviceName);
Set<ServiceRestMetadata> metadata = Collections.emptySet();
try {
String serviceRestMetadataJsonConfig = dubboMetadataService.getServiceRestMetadata();
if(StringUtils.hasText(serviceRestMetadataJsonConfig)) {
metadata = objectMapper.readValue(serviceRestMetadataJsonConfig,
TypeFactory.defaultInstance().constructCollectionType(LinkedHashSet.class, ServiceRestMetadata.class));
}
} catch (Exception e) {
if (logger.isErrorEnabled()) {
logger.error(e.getMessage(), e);
Set<ServiceRestMetadata> metadata = emptySet();
DubboMetadataService dubboMetadataService = dubboMetadataConfigServiceProxy.getProxy(serviceName);
if (dubboMetadataService != null) {
try {
String serviceRestMetadataJsonConfig = dubboMetadataService.getServiceRestMetadata();
if (hasText(serviceRestMetadataJsonConfig)) {
metadata = objectMapper.readValue(serviceRestMetadataJsonConfig,
TypeFactory.defaultInstance().constructCollectionType(LinkedHashSet.class, ServiceRestMetadata.class));
}
} catch (Exception e) {
if (logger.isErrorEnabled()) {
logger.error(e.getMessage(), e);
}
}
}
return metadata;
@ -292,8 +464,16 @@ public class DubboServiceMetadataRepository {
private void initSubscribedServices() {
// If subscribes all services
if (ALL_DUBBO_SERVICES.equalsIgnoreCase(dubboCloudProperties.getSubscribedServices())) {
subscribedServices = new HashSet<>(discoveryClient.getServices());
if (ALL_DUBBO_SERVICES.equals(dubboCloudProperties.getSubscribedServices())) {
List<String> services = discoveryClient.getServices();
subscribedServices = new HashSet<>(services);
if (logger.isWarnEnabled()) {
logger.warn("Current application will subscribe all services(size:{}) in registry, " +
"a lot of memory and CPU cycles may be used, " +
"thus it's strongly recommend you using the externalized property '{}' " +
"to specify the services",
subscribedServices.size(), "dubbo.cloud.subscribed-services");
}
} else {
subscribedServices = new HashSet<>(dubboCloudProperties.subscribedServices());
}
@ -305,23 +485,33 @@ public class DubboServiceMetadataRepository {
subscribedServices.remove(currentApplicationName);
}
private void initDubboServiceKeysRepository() {
private void initSubscribedDubboMetadataServices() {
// clear subscribedDubboMetadataServiceURLs
subscribedDubboMetadataServiceURLs.clear();
subscribedServices.stream()
.map(discoveryClient::getInstances)
.filter(this::isNotEmpty)
.forEach(serviceInstances -> {
ServiceInstance serviceInstance = serviceInstances.get(0);
buildURLs(serviceInstance).forEach(url -> {
String serviceKey = url.getServiceKey();
String serviceName = url.getParameter(APPLICATION_KEY);
dubboServiceKeysRepository.put(serviceKey, serviceName);
getDubboMetadataServiceURLs(serviceInstance).forEach(dubboMetadataServiceURL -> {
initSubscribedDubboMetadataServiceURLs(dubboMetadataServiceURL);
initDubboMetadataServiceProxy(dubboMetadataServiceURL);
});
});
}
private void retainAvailableSubscribedServices() {
// dubboServiceKeysRepository.values() returns the available services(possible duplicated ones)
subscribedServices = new HashSet<>(dubboServiceKeysRepository.values());
private void initSubscribedDubboMetadataServiceURLs(URL dubboMetadataServiceURL) {
// add subscriptions
String serviceKey = dubboMetadataServiceURL.getServiceKey();
subscribedDubboMetadataServiceURLs.add(serviceKey, dubboMetadataServiceURL);
}
private void initDubboMetadataServiceProxy(URL dubboMetadataServiceURL) {
String serviceName = dubboMetadataServiceURL.getParameter(APPLICATION_KEY);
String version = dubboMetadataServiceURL.getParameter(VERSION_KEY);
// Initialize DubboMetadataService with right version
dubboMetadataConfigServiceProxy.initProxy(serviceName, version);
}
private void initDubboRestServiceMetadataRepository() {

@ -24,24 +24,30 @@ import org.apache.dubbo.registry.support.FailbackRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataService;
import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataServiceProxy;
import org.springframework.cloud.alibaba.dubbo.util.JSONUtils;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static java.util.Collections.emptyList;
import static java.util.Collections.singleton;
import static org.apache.dubbo.common.Constants.APPLICATION_KEY;
import static org.apache.dubbo.common.Constants.GROUP_KEY;
import static org.apache.dubbo.common.Constants.PROTOCOL_KEY;
import static org.apache.dubbo.common.Constants.PROVIDER_SIDE;
import static org.apache.dubbo.common.Constants.SIDE_KEY;
import static org.springframework.util.ObjectUtils.isEmpty;
import static org.apache.dubbo.common.Constants.VERSION_KEY;
import static org.springframework.util.StringUtils.hasText;
/**
@ -56,6 +62,10 @@ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
*/
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();
private static final Set<String> schedulerTasks = new HashSet<>();
protected final Logger logger = LoggerFactory.getLogger(getClass());
/**
@ -65,14 +75,27 @@ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
private final DiscoveryClient discoveryClient;
private final DubboServiceMetadataRepository repository;
private final DubboMetadataServiceProxy dubboMetadataConfigServiceProxy;
private final JSONUtils jsonUtils;
protected final ScheduledExecutorService servicesLookupScheduler;
public AbstractSpringCloudRegistry(URL url,
DiscoveryClient discoveryClient,
DubboServiceMetadataRepository dubboServiceMetadataRepository,
DubboMetadataServiceProxy dubboMetadataConfigServiceProxy,
JSONUtils jsonUtils,
ScheduledExecutorService servicesLookupScheduler) {
super(url);
this.servicesLookupInterval = url.getParameter(SERVICES_LOOKUP_INTERVAL_PARAM_NAME, 60L);
this.discoveryClient = discoveryClient;
this.repository = dubboServiceMetadataRepository;
this.dubboMetadataConfigServiceProxy = dubboMetadataConfigServiceProxy;
this.jsonUtils = jsonUtils;
this.servicesLookupScheduler = servicesLookupScheduler;
}
@ -122,108 +145,138 @@ public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
@Override
public final void doSubscribe(URL url, NotifyListener listener) {
Set<String> serviceNames = getServiceNames(url);
doSubscribe(url, listener, serviceNames);
}
@Override
public final void doUnsubscribe(URL url, NotifyListener listener) {
if (isAdminProtocol(url)) {
shutdownServiceNamesLookup();
if (isAdminURL(url)) {
// TODO in future
} else if (isDubboMetadataServiceURL(url)) { // for DubboMetadataService
subscribeDubboMetadataServiceURLs(url, listener);
} else { // for general Dubbo Services
subscribeDubboServiceURLs(url, listener);
}
}
@Override
public boolean isAvailable() {
return !discoveryClient.getServices().isEmpty();
protected void subscribeDubboServiceURLs(URL url, NotifyListener listener) {
doSubscribeDubboServiceURLs(url, listener);
submitSchedulerTaskIfAbsent(url, listener);
}
protected void shutdownServiceNamesLookup() {
if (servicesLookupScheduler != null) {
servicesLookupScheduler.shutdown();
private void submitSchedulerTaskIfAbsent(URL url, NotifyListener listener) {
String taskId = url.toIdentityString();
if (schedulerTasks.add(taskId)) {
schedule(() -> doSubscribeDubboServiceURLs(url, listener));
}
}
private Set<String> filterServiceNames(Collection<String> serviceNames) {
return new LinkedHashSet<>(filter(serviceNames, this::supports));
protected void doSubscribeDubboServiceURLs(URL url, NotifyListener listener) {
Set<String> subscribedServices = repository.getSubscribedServices();
subscribedServices.stream()
.map(dubboMetadataConfigServiceProxy::getProxy)
.filter(Objects::nonNull)
.forEach(dubboMetadataService -> {
List<URL> exportedURLs = getExportedURLs(dubboMetadataService, url);
List<URL> allSubscribedURLs = new LinkedList<>();
for (URL exportedURL : exportedURLs) {
String serviceName = exportedURL.getParameter(APPLICATION_KEY);
List<ServiceInstance> serviceInstances = getServiceInstances(serviceName);
String protocol = exportedURL.getProtocol();
List<URL> subscribedURLs = new LinkedList<>();
serviceInstances.forEach(serviceInstance -> {
Integer port = repository.getDubboProtocolPort(serviceInstance, protocol);
String host = serviceInstance.getHost();
if (port == null) {
if (logger.isWarnEnabled()) {
logger.warn("The protocol[{}] port of Dubbo service instance[host : {}] " +
"can't be resolved", protocol, host);
}
} else {
URL subscribedURL = new URL(protocol, host, port, exportedURL.getParameters());
subscribedURLs.add(subscribedURL);
}
});
if (logger.isDebugEnabled()) {
logger.debug("The subscribed URL[{}] will notify all URLs : {}", url, subscribedURLs);
}
allSubscribedURLs.addAll(subscribedURLs);
}
listener.notify(allSubscribedURLs);
});
}
protected abstract boolean supports(String serviceName);
protected final Set<String> getAllServiceNames() {
return new LinkedHashSet<>(discoveryClient.getServices());
private List<ServiceInstance> getServiceInstances(String serviceName) {
return hasText(serviceName) ? doGetServiceInstances(serviceName) : emptyList();
}
/**
* Get the service names from the specified {@link URL url}
*
* @param url {@link URL}
* @return non-null
*/
private Set<String> getServiceNames(URL url) {
if (isAdminProtocol(url)) {
return getServiceNamesForOps(url);
} else {
return singleton(getServiceName(url));
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;
}
protected boolean isAdminProtocol(URL url) {
return Constants.ADMIN_PROTOCOL.equals(url.getProtocol());
private List<URL> getExportedURLs(DubboMetadataService dubboMetadataService, URL url) {
String serviceInterface = url.getServiceInterface();
String group = url.getParameter(GROUP_KEY);
String version = url.getParameter(VERSION_KEY);
// The subscribed protocol may be null
String subscribedProtocol = url.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());
}
/**
* Get the service names for Dubbo OPS
*
* @param url {@link URL}
* @return non-null
*/
protected Set<String> getServiceNamesForOps(URL url) {
Set<String> serviceNames = getAllServiceNames();
return filterServiceNames(serviceNames);
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);
listener.notify(urls);
}
protected abstract String getServiceName(URL url);
private void doSubscribe(final URL url, final NotifyListener listener, final Collection<String> serviceNames) {
subscribe(url, listener, serviceNames);
schedule(() -> {
subscribe(url, listener, serviceNames);
});
@Override
public final void doUnsubscribe(URL url, NotifyListener listener) {
if (isAdminURL(url)) {
shutdownServiceNamesLookup();
}
}
protected ScheduledFuture<?> schedule(Runnable runnable) {
return this.servicesLookupScheduler.scheduleAtFixedRate(runnable, servicesLookupInterval,
servicesLookupInterval, TimeUnit.SECONDS);
@Override
public boolean isAvailable() {
return !discoveryClient.getServices().isEmpty();
}
protected List<ServiceInstance> getServiceInstances(String serviceName) {
return hasText(serviceName) ? discoveryClient.getInstances(serviceName) : emptyList();
protected void shutdownServiceNamesLookup() {
if (servicesLookupScheduler != null) {
servicesLookupScheduler.shutdown();
}
}
private void subscribe(final URL url, final NotifyListener listener, final Collection<String> serviceNames) {
for (String serviceName : serviceNames) {
List<ServiceInstance> serviceInstances = getServiceInstances(serviceName);
if (!isEmpty(serviceInstances)) {
notifySubscriber(url, listener, serviceInstances);
}
}
protected boolean isAdminURL(URL url) {
return Constants.ADMIN_PROTOCOL.equals(url.getProtocol());
}
/**
* Notify the Healthy {@link ServiceInstance service instance} to subscriber.
*
* @param url {@link URL}
* @param listener {@link NotifyListener}
* @param serviceInstances all {@link ServiceInstance instances}
*/
protected abstract void notifySubscriber(URL url, NotifyListener listener, List<ServiceInstance> serviceInstances);
protected boolean isDubboMetadataServiceURL(URL url) {
return DUBBO_METADATA_SERVICE_CLASS_NAME.equals(url.getServiceInterface());
}
protected <T> Collection<T> filter(Collection<T> collection, Predicate<T> filter) {
return collection.stream()
.filter(filter)
.collect(Collectors.toList());
protected ScheduledFuture<?> schedule(Runnable runnable) {
return this.servicesLookupScheduler.scheduleAtFixedRate(runnable, servicesLookupInterval,
servicesLookupInterval, TimeUnit.SECONDS);
}
}

@ -17,16 +17,14 @@
package org.springframework.cloud.alibaba.dubbo.registry;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.RegistryFactory;
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataServiceProxy;
import org.springframework.cloud.alibaba.dubbo.util.JSONUtils;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.stream.Collectors;
/**
* Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose protocol is "spring-cloud"
@ -38,38 +36,21 @@ public class SpringCloudRegistry extends AbstractSpringCloudRegistry {
private final DubboServiceMetadataRepository dubboServiceMetadataRepository;
public SpringCloudRegistry(URL url, DiscoveryClient discoveryClient,
ScheduledExecutorService servicesLookupScheduler,
DubboServiceMetadataRepository dubboServiceMetadataRepository) {
super(url, discoveryClient, servicesLookupScheduler);
DubboServiceMetadataRepository dubboServiceMetadataRepository,
DubboMetadataServiceProxy dubboMetadataConfigServiceProxy,
JSONUtils jsonUtils,
ScheduledExecutorService servicesLookupScheduler) {
super(url, discoveryClient, dubboServiceMetadataRepository, dubboMetadataConfigServiceProxy, jsonUtils, servicesLookupScheduler);
this.dubboServiceMetadataRepository = dubboServiceMetadataRepository;
}
@Override
protected void doRegister0(URL url) {
dubboServiceMetadataRepository.registerURL(url);
dubboServiceMetadataRepository.exportURL(url);
}
@Override
protected void doUnregister0(URL url) {
dubboServiceMetadataRepository.unregisterURL(url);
}
@Override
protected boolean supports(String serviceName) {
return dubboServiceMetadataRepository.isSubscribedService(serviceName);
}
@Override
protected String getServiceName(URL url) {
return dubboServiceMetadataRepository.getServiceName(url);
}
@Override
protected void notifySubscriber(URL url, NotifyListener listener, List<ServiceInstance> serviceInstances) {
List<URL> urls = serviceInstances.stream()
.map(dubboServiceMetadataRepository::buildURLs)
.flatMap(List::stream)
.collect(Collectors.toList());
notify(url, listener, urls);
dubboServiceMetadataRepository.unexportURL(url);
}
}

@ -22,6 +22,8 @@ import org.apache.dubbo.registry.Registry;
import org.apache.dubbo.registry.RegistryFactory;
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataServiceProxy;
import org.springframework.cloud.alibaba.dubbo.util.JSONUtils;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.context.ConfigurableApplicationContext;
@ -50,6 +52,10 @@ public class SpringCloudRegistryFactory implements RegistryFactory {
private DubboServiceMetadataRepository dubboServiceMetadataRepository;
private DubboMetadataServiceProxy dubboMetadataConfigServiceProxy;
private JSONUtils jsonUtils;
private volatile boolean initialized = false;
public SpringCloudRegistryFactory() {
@ -63,12 +69,15 @@ public class SpringCloudRegistryFactory implements RegistryFactory {
}
this.discoveryClient = applicationContext.getBean(DiscoveryClient.class);
this.dubboServiceMetadataRepository = applicationContext.getBean(DubboServiceMetadataRepository.class);
this.dubboMetadataConfigServiceProxy = applicationContext.getBean(DubboMetadataServiceProxy.class);
this.jsonUtils = applicationContext.getBean(JSONUtils.class);
}
@Override
public Registry getRegistry(URL url) {
init();
return new SpringCloudRegistry(url, discoveryClient, servicesLookupScheduler, dubboServiceMetadataRepository);
return new SpringCloudRegistry(url, discoveryClient, dubboServiceMetadataRepository,
dubboMetadataConfigServiceProxy, jsonUtils, servicesLookupScheduler);
}
public static void setApplicationContext(ConfigurableApplicationContext applicationContext) {

@ -62,9 +62,9 @@ public class DubboGenericServiceFactory {
return referenceBean == null ? null : referenceBean.get();
}
public GenericService create(String serviceName, Class<?> serviceClass) {
public GenericService create(String serviceName, Class<?> serviceClass, String version) {
String interfaceName = serviceClass.getName();
ReferenceBean<GenericService> referenceBean = build(interfaceName, serviceName, null, emptyMap());
ReferenceBean<GenericService> referenceBean = build(interfaceName, version, serviceName, emptyMap());
return referenceBean.get();
}

@ -16,21 +16,61 @@
*/
package org.springframework.cloud.alibaba.dubbo.service;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Dubbo Metadata Service
* Dubbo Metadata Service is a core interface for service subscribers,
* it must keep the stable of structure in every evolution , makes sure all subscribers' compatibility.
* <p>
* The interface contract's version must be {@link #VERSION} constant and group must be current Dubbo application name
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public interface DubboMetadataService {
/**
* Get The json content of {@link ServiceRestMetadata} {@link Set}
* Current version of the interface contract
*/
String VERSION = "1.0.0";
/**
* Get the json content of {@link ServiceRestMetadata} {@link Set}
*
* @return <code>null</code> if present
*/
String getServiceRestMetadata();
/**
* Get all exported {@link URL#getServiceKey() service keys}
*
* @return non-null read-only {@link Set}
*/
Set<String> getAllServiceKeys();
/**
* Get all exported Dubbo's {@link URL URLs} {@link Map} whose key is the return value of
* {@link URL#getServiceKey()} method and value is the json content of List<URL> of {@link URL URLs}
*
* @return non-null read-only {@link Map}
*/
Map<String, String> getAllExportedURLs();
/**
* Get the json content of an exported List<URL> of {@link URL URLs} by the serviceInterface , group and version
*
* @param serviceInterface The class name of service interface
* @param group {@link Service#group() the service group} (optional)
* @param version {@link Service#version() the service version} (optional)
* @return non-null read-only {@link List}
* @see URL
*/
String getExportedURLs(String serviceInterface, String group, String version);
}

@ -16,16 +16,20 @@
*/
package org.springframework.cloud.alibaba.dubbo.service;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.ServiceConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PreDestroy;
import java.util.List;
import java.util.function.Supplier;
/**
@ -42,12 +46,12 @@ public class DubboMetadataServiceExporter {
private ApplicationConfig applicationConfig;
@Autowired
private DubboMetadataService dubboMetadataService;
private ObjectProvider<DubboMetadataService> dubboMetadataService;
@Autowired
private Supplier<ProtocolConfig> protocolConfigSupplier;
@Value("${spring.application.name:application}")
@Value("${spring.application.name:${dubbo.application.name:application}}")
private String currentApplicationName;
/**
@ -57,33 +61,39 @@ public class DubboMetadataServiceExporter {
/**
* export {@link DubboMetadataService} as Dubbo service
*
* @return the exported {@link URL URLs}
*/
public void export() {
public List<URL> export() {
if (serviceConfig != null && serviceConfig.isExported()) {
return;
}
if (serviceConfig == null || !serviceConfig.isExported()) {
serviceConfig = new ServiceConfig<>();
serviceConfig = new ServiceConfig<>();
serviceConfig.setInterface(DubboMetadataService.class);
// Use current Spring application name as the Dubbo Service version
serviceConfig.setVersion(currentApplicationName);
serviceConfig.setRef(dubboMetadataService);
serviceConfig.setApplication(applicationConfig);
serviceConfig.setProtocol(protocolConfigSupplier.get());
serviceConfig.setInterface(DubboMetadataService.class);
// Use DubboMetadataService.VERSION as the Dubbo Service version
serviceConfig.setVersion(DubboMetadataService.VERSION);
// Use current Spring application name as the Dubbo Service group
serviceConfig.setGroup(currentApplicationName);
serviceConfig.setRef(dubboMetadataService.getIfAvailable());
serviceConfig.setApplication(applicationConfig);
serviceConfig.setProtocol(protocolConfigSupplier.get());
serviceConfig.export();
serviceConfig.export();
if (logger.isInfoEnabled()) {
logger.info("The Dubbo service[{}] has been exported.", serviceConfig.toString());
if (logger.isInfoEnabled()) {
logger.info("The Dubbo service[{}] has been exported.", serviceConfig.toString());
}
}
return serviceConfig.getExportedUrls();
}
/**
* unexport {@link DubboMetadataService}
*/
@PreDestroy
public void unexport() {
if (serviceConfig == null || serviceConfig.isUnexported()) {

@ -18,8 +18,12 @@ package org.springframework.cloud.alibaba.dubbo.service;
import org.apache.dubbo.rpc.service.GenericService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.stream.Stream;
/**
* {@link DubboMetadataService} {@link InvocationHandler}
@ -28,27 +32,29 @@ import java.lang.reflect.Method;
*/
class DubboMetadataServiceInvocationHandler implements InvocationHandler {
/**
* The method name of {@link DubboMetadataService#getServiceRestMetadata()}
*/
private static final String METHOD_NAME = "getServiceRestMetadata";
private static final String[] PARAMETER_TYPES = new String[0];
private static final String[] PARAMETER_VALUES = new String[0];
private final Logger logger = LoggerFactory.getLogger(getClass());
private final GenericService genericService;
public DubboMetadataServiceInvocationHandler(String serviceName, DubboGenericServiceFactory dubboGenericServiceFactory) {
this.genericService = dubboGenericServiceFactory.create(serviceName, DubboMetadataService.class);
public DubboMetadataServiceInvocationHandler(String serviceName, String version, DubboGenericServiceFactory dubboGenericServiceFactory) {
this.genericService = dubboGenericServiceFactory.create(serviceName, DubboMetadataService.class, version);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String methodName = method.getName();
if (METHOD_NAME.equals(methodName)) {
return genericService.$invoke(methodName, PARAMETER_TYPES, PARAMETER_VALUES);
Object returnValue = null;
try {
returnValue = genericService.$invoke(method.getName(), getParameterTypes(method), args);
} catch (Throwable e) {
if (logger.isErrorEnabled()) {
logger.error(e.getMessage(), e);
}
}
return method.invoke(proxy, args);
return returnValue;
}
private String[] getParameterTypes(Method method) {
Class<?>[] parameterTypes = method.getParameterTypes();
return Stream.of(parameterTypes).map(Class::getName).toArray(length -> new String[length]);
}
}

@ -17,35 +17,68 @@
package org.springframework.cloud.alibaba.dubbo.service;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.DisposableBean;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import static java.lang.reflect.Proxy.newProxyInstance;
/**
* The proxy of {@link DubboMetadataService}
*/
public class DubboMetadataServiceProxy implements BeanClassLoaderAware {
public class DubboMetadataServiceProxy implements BeanClassLoaderAware, DisposableBean {
private final DubboGenericServiceFactory dubboGenericServiceFactory;
private ClassLoader classLoader;
private final Map<String, DubboMetadataService> dubboMetadataServiceCache = new ConcurrentHashMap<>();
public DubboMetadataServiceProxy(DubboGenericServiceFactory dubboGenericServiceFactory) {
this.dubboGenericServiceFactory = dubboGenericServiceFactory;
}
/**
* New proxy instance of {@link DubboMetadataService} via the specified service name
* Initializes {@link DubboMetadataService}'s Proxy
*
* @param serviceName the service name
* @param version the service version
* @return a {@link DubboMetadataService} proxy
*/
public DubboMetadataService newProxy(String serviceName) {
return (DubboMetadataService) newProxyInstance(classLoader, new Class[]{DubboMetadataService.class},
new DubboMetadataServiceInvocationHandler(serviceName, dubboGenericServiceFactory));
public DubboMetadataService initProxy(String serviceName, String version) {
return dubboMetadataServiceCache.computeIfAbsent(serviceName, name -> newProxy(name, version));
}
/**
* Get a proxy instance of {@link DubboMetadataService} via the specified service name
*
* @param serviceName the service name
* @return a {@link DubboMetadataService} proxy
*/
public DubboMetadataService getProxy(String serviceName) {
return dubboMetadataServiceCache.get(serviceName);
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
public void destroy() throws Exception {
dubboMetadataServiceCache.clear();
}
/**
* New a proxy instance of {@link DubboMetadataService} via the specified service name
*
* @param serviceName the service name
* @param version the service version
* @return a {@link DubboMetadataService} proxy
*/
protected DubboMetadataService newProxy(String serviceName, String version) {
return (DubboMetadataService) newProxyInstance(classLoader, new Class[]{DubboMetadataService.class},
new DubboMetadataServiceInvocationHandler(serviceName, version, dubboGenericServiceFactory));
}
}

@ -0,0 +1,96 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.dubbo.service;
import org.apache.dubbo.common.URL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
import org.springframework.cloud.alibaba.dubbo.util.JSONUtils;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static java.util.Collections.unmodifiableMap;
import static org.springframework.util.CollectionUtils.isEmpty;
/**
* Introspective {@link DubboMetadataService} implementation
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class IntrospectiveDubboMetadataService implements DubboMetadataService {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private ObjectProvider<DubboServiceMetadataRepository> dubboServiceMetadataRepository;
@Autowired
private JSONUtils jsonUtils;
@Override
public String getServiceRestMetadata() {
Set<ServiceRestMetadata> serviceRestMetadata = getRepository().getServiceRestMetadata();
String serviceRestMetadataJsonConfig = null;
if (!isEmpty(serviceRestMetadata)) {
serviceRestMetadataJsonConfig = jsonUtils.toJSON(serviceRestMetadata);
}
return serviceRestMetadataJsonConfig;
}
@Override
public Set<String> getAllServiceKeys() {
return getRepository().getAllServiceKeys();
}
@Override
public Map<String, String> getAllExportedURLs() {
Map<String, List<URL>> allExportedUrls = getRepository().getAllExportedUrls();
if (isEmpty(allExportedUrls)) {
if (logger.isDebugEnabled()) {
logger.debug("There is no registered URL.");
}
return Collections.emptyMap();
}
Map<String, String> result = new HashMap<>();
allExportedUrls.forEach((serviceKey, urls) -> {
result.put(serviceKey, jsonUtils.toJSON(urls));
});
return unmodifiableMap(result);
}
@Override
public String getExportedURLs(String serviceInterface, String group, String version) {
List<URL> urls = getRepository().getExportedURLs(serviceInterface, group, version);
return jsonUtils.toJSON(urls);
}
private DubboServiceMetadataRepository getRepository() {
return dubboServiceMetadataRepository.getIfAvailable();
}
}

@ -1,66 +0,0 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.dubbo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
import org.springframework.cloud.alibaba.dubbo.util.JSONUtils;
import org.springframework.util.CollectionUtils;
import java.util.LinkedHashSet;
import java.util.Set;
import static org.springframework.util.ObjectUtils.isEmpty;
/**
* Publishing {@link DubboMetadataService} implementation
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class PublishingDubboMetadataService implements DubboMetadataService {
/**
* A Map to store REST metadata temporary, its' key is the special service name for a Dubbo service,
* the value is a JSON content of JAX-RS or Spring MVC REST metadata from the annotated methods.
*/
private final Set<ServiceRestMetadata> serviceRestMetadata = new LinkedHashSet<>();
@Autowired
private JSONUtils jsonUtils;
/**
* Publish the {@link Set} of {@link ServiceRestMetadata}
*
* @param serviceRestMetadataSet the {@link Set} of {@link ServiceRestMetadata}
*/
public void publishServiceRestMetadata(Set<ServiceRestMetadata> serviceRestMetadataSet) {
for (ServiceRestMetadata serviceRestMetadata : serviceRestMetadataSet) {
if (!CollectionUtils.isEmpty(serviceRestMetadata.getMeta())) {
this.serviceRestMetadata.add(serviceRestMetadata);
}
}
}
@Override
public String getServiceRestMetadata() {
String serviceRestMetadataJsonConfig = null;
if (!isEmpty(serviceRestMetadata)) {
serviceRestMetadataJsonConfig = jsonUtils.toJSON(serviceRestMetadata);
}
return serviceRestMetadataJsonConfig;
}
}

@ -16,6 +16,8 @@
*/
package org.springframework.cloud.alibaba.dubbo.util;
import org.apache.dubbo.common.URL;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
@ -25,8 +27,10 @@ import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
/**
* JSON Utilities class
@ -44,6 +48,10 @@ public class JSONUtils {
this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
}
public String toJSON(Collection<URL> urls) {
return toJSON(urls.stream().map(URL::toFullString).collect(Collectors.toSet()));
}
public String toJSON(Object object) {
String jsonContent = null;
try {
@ -56,6 +64,11 @@ public class JSONUtils {
return jsonContent;
}
public List<URL> toURLs(String urlsJSON) {
List<String> list = toList(urlsJSON);
return list.stream().map(URL::valueOf).collect(Collectors.toList());
}
public List<String> toList(String json) {
List<String> list = Collections.emptyList();
try {

Loading…
Cancel
Save