mercyblitz 6 years ago
parent 514fef53f5
commit e48863640a

@ -20,12 +20,15 @@ import org.apache.dubbo.common.utils.Assert;
import org.apache.dubbo.config.spring.util.PropertySourcesUtils;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.cloud.alibaba.dubbo.registry.handler.DubboRegistryServiceIdHandler;
import org.springframework.cloud.alibaba.dubbo.registry.handler.StandardDubboRegistryServiceIdHandler;
import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceExecutionContextFactory;
import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceFactory;
import org.springframework.cloud.alibaba.dubbo.service.parameter.PathVariableServiceParameterResolver;
import org.springframework.cloud.alibaba.dubbo.service.parameter.RequestBodyServiceParameterResolver;
import org.springframework.cloud.alibaba.dubbo.service.parameter.RequestHeaderServiceParameterResolver;
import org.springframework.cloud.alibaba.dubbo.service.parameter.RequestParamServiceParameterResolver;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@ -67,6 +70,11 @@ public class DubboServiceAutoConfiguration {
static class ParameterResolversConfiguration {
}
@Bean
@ConditionalOnMissingBean
public DubboRegistryServiceIdHandler dubboRegistryServiceIdHandler(ConfigurableApplicationContext context) {
return new StandardDubboRegistryServiceIdHandler(context);
}
/**
* Bugfix code for an issue : https://github.com/apache/incubator-dubbo-spring-boot-project/issues/459

@ -30,16 +30,16 @@ import java.util.Set;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ServiceRestMetadata {
private String name;
private String url;
private Set<RestMethodMetadata> meta;
public String getName() {
return name;
public String getUrl() {
return url;
}
public void setName(String name) {
this.name = name;
public void setUrl(String url) {
this.url = url;
}
public Set<RestMethodMetadata> getMeta() {
@ -55,12 +55,12 @@ public class ServiceRestMetadata {
if (this == o) return true;
if (!(o instanceof ServiceRestMetadata)) return false;
ServiceRestMetadata that = (ServiceRestMetadata) o;
return Objects.equals(name, that.name) &&
return Objects.equals(url, that.url) &&
Objects.equals(meta, that.meta);
}
@Override
public int hashCode() {
return Objects.hash(name, meta);
return Objects.hash(url, meta);
}
}

@ -28,7 +28,6 @@ import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
import org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry;
import org.springframework.util.ClassUtils;
import java.lang.reflect.Method;
@ -115,10 +114,10 @@ public class DubboServiceBeanMetadataResolver implements BeanClassLoaderAware, S
List<URL> urls = serviceBean.getExportedUrls();
urls.stream()
.map(SpringCloudRegistry::getServiceName)
.forEach(serviceName -> {
.map(URL::toString)
.forEach(url -> {
ServiceRestMetadata metadata = new ServiceRestMetadata();
metadata.setName(serviceName);
metadata.setUrl(url);
metadata.setMeta(methodRestMetadata);
serviceRestMetadata.add(metadata);
});

@ -18,10 +18,11 @@ package org.springframework.cloud.alibaba.dubbo.registry;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.NetUtils;
import org.springframework.cloud.alibaba.dubbo.registry.handler.DubboRegistryServiceIdHandler;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.context.ConfigurableApplicationContext;
import java.util.LinkedHashMap;
@ -29,20 +30,49 @@ import java.util.LinkedHashMap;
* Abstract {@link RegistrationFactory} implementation
* <p>
*
* @param <T> The subclass of {@link Registration}
* @param <R> The subclass of {@link Registration}
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public abstract class AbstractRegistrationFactory<T extends Registration> implements RegistrationFactory<T> {
public abstract class AbstractRegistrationFactory<R extends Registration> implements RegistrationFactory<R> {
protected ServiceInstance createServiceInstance(String serviceName, URL url) {
public final R create(URL url, ConfigurableApplicationContext applicationContext) {
ServiceInstance serviceInstance = createServiceInstance(url, applicationContext);
return create(url, applicationContext, serviceInstance);
}
/**
* Sub-class should override this method to create an instance of {@link R}
*
* @param url The Dubbo's {@link URL}
* @param applicationContext {@link ConfigurableApplicationContext}
* @param serviceInstance {@link ServiceInstance}
* @return nullable
*/
protected abstract R create(URL url, ConfigurableApplicationContext applicationContext, ServiceInstance serviceInstance);
/**
* Create an instance {@link ServiceInstance}. This method maybe override by sub-class.
*
* @param url The Dubbo's {@link URL}
* @param applicationContext {@link ConfigurableApplicationContext}
* @return an instance {@link ServiceInstance}
*/
protected ServiceInstance createServiceInstance(URL url, ConfigurableApplicationContext applicationContext) {
String serviceId = createServiceId(url, applicationContext);
// Append default category if absent
String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
URL newURL = url.addParameter(Constants.CATEGORY_KEY, category);
newURL = newURL.addParameter(Constants.PROTOCOL_KEY, url.getProtocol());
String ip = NetUtils.getLocalHost();
String ip = url.getIp();
int port = newURL.getParameter(Constants.BIND_PORT_KEY, url.getPort());
DefaultServiceInstance serviceInstance = new DefaultServiceInstance(url.toIdentityString(), serviceName, ip, port, false);
DefaultServiceInstance serviceInstance = new DefaultServiceInstance(url.toIdentityString(), serviceId, ip, port, false);
serviceInstance.getMetadata().putAll(new LinkedHashMap<>(newURL.getParameters()));
return serviceInstance;
}
protected String createServiceId(URL url, ConfigurableApplicationContext applicationContext) {
DubboRegistryServiceIdHandler handler = applicationContext.getBean(DubboRegistryServiceIdHandler.class);
return handler.createServiceId(url);
}
}

@ -17,8 +17,9 @@
package org.springframework.cloud.alibaba.dubbo.registry;
import org.apache.dubbo.common.URL;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
/**
* Default {@link RegistrationFactory}
@ -28,7 +29,7 @@ import org.springframework.context.ApplicationContext;
public class DefaultRegistrationFactory extends AbstractRegistrationFactory<Registration> {
@Override
public Registration create(String serviceName, URL url, ApplicationContext applicationContext) {
return new DelegatingRegistration(createServiceInstance(serviceName, url));
protected Registration create(URL url, ConfigurableApplicationContext applicationContext, ServiceInstance serviceInstance) {
return new DelegatingRegistration(serviceInstance);
}
}

@ -18,24 +18,23 @@ package org.springframework.cloud.alibaba.dubbo.registry;
import org.apache.dubbo.common.URL;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
/**
* {@link Registration} Factory to createServiceInstance a instance of {@link Registration}
*
* @param <T> The subclass of {@link Registration}
* @param <R> The subclass of {@link Registration}
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public interface RegistrationFactory<T extends Registration> {
public interface RegistrationFactory<R extends Registration> {
/**
* Create a instance of {@link T}
* Create a instance of {@link R}
*
* @param serviceName The service name of Dubbo service interface
* @param url The Dubbo's URL
* @param applicationContext {@link ApplicationContext}
* @return a instance of {@link T}
* @param url The Dubbo's {@link URL}
* @param applicationContext {@link ConfigurableApplicationContext}
* @return a instance of {@link R}, if null, it indicates the registration will not be executed.
*/
T create(String serviceName, URL url, ApplicationContext applicationContext);
R create(URL url, ConfigurableApplicationContext applicationContext);
}

@ -18,44 +18,35 @@ package org.springframework.cloud.alibaba.dubbo.registry;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.NamedThreadFactory;
import org.apache.dubbo.common.utils.UrlUtils;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.RegistryFactory;
import org.apache.dubbo.registry.support.FailbackRegistry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.alibaba.dubbo.registry.handler.DubboRegistryServiceIdHandler;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.core.ResolvableType;
import org.springframework.util.StringUtils;
import org.springframework.context.ConfigurableApplicationContext;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import static com.alibaba.dubbo.common.Constants.CONFIGURATORS_CATEGORY;
import static com.alibaba.dubbo.common.Constants.CONSUMERS_CATEGORY;
import static com.alibaba.dubbo.common.Constants.PROVIDERS_CATEGORY;
import static com.alibaba.dubbo.common.Constants.ROUTERS_CATEGORY;
import static java.lang.Long.getLong;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static org.springframework.beans.BeanUtils.instantiateClass;
import static org.springframework.core.ResolvableType.forInstance;
import static org.springframework.core.ResolvableType.forType;
import static org.springframework.core.io.support.SpringFactoriesLoader.loadFactoryNames;
import static org.springframework.util.ClassUtils.isPresent;
import static org.springframework.util.ClassUtils.resolveClassName;
import static java.util.Collections.singletonList;
import static org.apache.dubbo.common.Constants.CONFIGURATORS_CATEGORY;
import static org.apache.dubbo.common.Constants.CONSUMERS_CATEGORY;
import static org.apache.dubbo.common.Constants.PROVIDERS_CATEGORY;
import static org.apache.dubbo.common.Constants.PROVIDER_SIDE;
import static org.apache.dubbo.common.Constants.ROUTERS_CATEGORY;
import static org.apache.dubbo.common.Constants.SIDE_KEY;
/**
* Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose protocol is "spring-cloud"
@ -64,169 +55,96 @@ import static org.springframework.util.ClassUtils.resolveClassName;
*/
public class SpringCloudRegistry extends FailbackRegistry {
/**
* The parameter name of {@link #allServicesLookupInterval}
*/
public static final String ALL_SERVICES_LOOKUP_INTERVAL_PARAM_NAME = "dubbo.all.services.lookup.interval";
/**
* The parameter name of {@link #registeredServicesLookupInterval}
*/
public static final String REGISTERED_SERVICES_LOOKUP_INTERVAL_PARAM_NAME = "dubbo.registered.services.lookup.interval";
/**
* All supported categories
*/
private static final String[] ALL_SUPPORTED_CATEGORIES = of(
public static final String[] ALL_SUPPORTED_CATEGORIES = of(
PROVIDERS_CATEGORY,
CONSUMERS_CATEGORY,
ROUTERS_CATEGORY,
CONFIGURATORS_CATEGORY
);
private static final int CATEGORY_INDEX = 0;
private static final int SERVICE_INTERFACE_INDEX = CATEGORY_INDEX + 1;
private static final int SERVICE_VERSION_INDEX = SERVICE_INTERFACE_INDEX + 1;
private static final int SERVICE_GROUP_INDEX = SERVICE_VERSION_INDEX + 1;
private static final String WILDCARD = "*";
private final Logger logger = LoggerFactory.getLogger(getClass());
/**
* The interval in second of lookup service names(only for Dubbo-OPS)
*/
private static final long ALL_SERVICES_LOOKUP_INTERVAL = getLong("dubbo.all.services.lookup.interval", 30);
private final long allServicesLookupInterval;
/**
* The interval in second of lookup regigered service instances
*/
private static final long REGISTERED_SERVICES_LOOKUP_INTERVAL = getLong("dubbo.registered.services.lookup.interval", 300);
private final long registeredServicesLookupInterval;
/**
* The {@link ScheduledExecutorService Scheduler} to lookup the registered services
*/
private static final ScheduledExecutorService registeredServicesLookupScheduler = newSingleThreadScheduledExecutor(new NamedThreadFactory("dubbo-registered-services-lookup-"));
private final ServiceRegistry<Registration> serviceRegistry;
/**
* The {@link ScheduledExecutorService Scheduler} to lookup all services (only for Dubbo-OPS)
*/
private static volatile ScheduledExecutorService allServicesLookupScheduler;
private final RegistrationFactory registrationFactory;
private final Logger logger = LoggerFactory.getLogger(getClass());
private final DiscoveryClient discoveryClient;
/**
* The separator for service name
*/
private static final String SERVICE_NAME_SEPARATOR = ":";
private final DubboRegistryServiceIdHandler dubboRegistryServiceIdHandler;
private final ApplicationContext applicationContext;
private final ScheduledExecutorService servicesLookupScheduler;
private final ServiceRegistry<Registration> serviceRegistry;
private final ConfigurableApplicationContext applicationContext;
private final DiscoveryClient discoveryClient;
private final RegistrationFactory registrationFactory;
public SpringCloudRegistry(URL url, ApplicationContext applicationContext) {
public SpringCloudRegistry(URL url,
ServiceRegistry<Registration> serviceRegistry,
RegistrationFactory registrationFactory,
DiscoveryClient discoveryClient,
ScheduledExecutorService servicesLookupScheduler,
ConfigurableApplicationContext applicationContext) {
super(url);
this.allServicesLookupInterval = url.getParameter(ALL_SERVICES_LOOKUP_INTERVAL_PARAM_NAME, 30L);
this.registeredServicesLookupInterval = url.getParameter(REGISTERED_SERVICES_LOOKUP_INTERVAL_PARAM_NAME, 300L);
this.serviceRegistry = serviceRegistry;
this.registrationFactory = registrationFactory;
this.discoveryClient = discoveryClient;
this.dubboRegistryServiceIdHandler = applicationContext.getBean(DubboRegistryServiceIdHandler.class);
this.applicationContext = applicationContext;
this.serviceRegistry = applicationContext.getBean(ServiceRegistry.class);
this.registrationFactory = buildRegistrationFactory(serviceRegistry, applicationContext.getClassLoader());
this.discoveryClient = applicationContext.getBean(DiscoveryClient.class);
applicationContext.getClassLoader();
this.servicesLookupScheduler = servicesLookupScheduler;
}
private RegistrationFactory buildRegistrationFactory(ServiceRegistry<Registration> serviceRegistry,
ClassLoader classLoader) {
RegistrationFactory registrationFactory = null;
List<String> factoryClassNames = loadFactoryNames(RegistrationFactory.class, classLoader);
ResolvableType serviceRegistryType = forInstance(serviceRegistry);
// Get first generic Class
Class<?> registrationClass = resolveGenericClass(serviceRegistryType, ServiceRegistry.class, 0);
for (String factoryClassName : factoryClassNames) {
if (isPresent(factoryClassName, classLoader)) { // ignore compilation issue
Class<?> factoryClass = resolveClassName(factoryClassName, classLoader);
ResolvableType registrationFactoryType = forType(factoryClass);
Class<?> actualRegistrationClass = resolveGenericClass(registrationFactoryType, RegistrationFactory.class, 0);
if (registrationClass.equals(actualRegistrationClass)) {
registrationFactory = (RegistrationFactory) instantiateClass(registrationFactoryType.getRawClass());
break;
}
}
}
if (registrationFactory == null) {
if (logger.isWarnEnabled()) {
logger.warn("{} implementation can't be resolved by ServiceRegistry[{}]",
registrationClass.getSimpleName(), serviceRegistry.getClass().getName());
}
registrationFactory = new DefaultRegistrationFactory();
} else {
if (logger.isInfoEnabled()) {
logger.info("{} has been resolved by ServiceRegistry[{}]",
registrationFactory.getClass().getName(), serviceRegistry.getClass().getName());
}
}
return registrationFactory;
protected boolean shouldRegister(Registration registration) {
Map<String, String> metadata = registration.getMetadata();
String side = metadata.get(SIDE_KEY);
return PROVIDER_SIDE.equals(side); // Only register the Provider.
}
private Class<?> resolveGenericClass(ResolvableType implementedType, Class<?> interfaceClass, int index) {
ResolvableType resolvableType = implementedType;
try {
OUTER:
while (true) {
ResolvableType[] interfaceTypes = resolvableType.getInterfaces();
for (ResolvableType interfaceType : interfaceTypes) {
if (interfaceType.resolve().equals(interfaceClass)) {
resolvableType = interfaceType;
break OUTER;
}
}
ResolvableType superType = resolvableType.getSuperType();
Class<?> superClass = superType.resolve();
if (Object.class.equals(superClass)) {
break;
}
resolvableType = superType;
}
} catch (Throwable e) {
resolvableType = ResolvableType.forType(void.class);
}
return resolvableType.resolveGeneric(index);
}
@Override
public void doRegister(URL url) {
final String serviceName = getServiceName(url);
final Registration registration = createRegistration(serviceName, url);
serviceRegistry.register(registration);
final Registration registration = createRegistration(url);
if (shouldRegister(registration)) {
serviceRegistry.register(registration);
}
}
@Override
public void doUnregister(URL url) {
final String serviceName = getServiceName(url);
final Registration registration = createRegistration(serviceName, url);
this.serviceRegistry.deregister(registration);
final Registration registration = createRegistration(url);
if (shouldRegister(registration)) {
this.serviceRegistry.deregister(registration);
}
}
@Override
public void doSubscribe(URL url, NotifyListener listener) {
List<String> serviceNames = getServiceNames(url, listener);
doSubscribe(url, listener, serviceNames);
this.registeredServicesLookupScheduler.scheduleAtFixedRate(new Runnable() {
this.servicesLookupScheduler.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
doSubscribe(url, listener, serviceNames);
}
}, REGISTERED_SERVICES_LOOKUP_INTERVAL, REGISTERED_SERVICES_LOOKUP_INTERVAL, TimeUnit.SECONDS);
}, registeredServicesLookupInterval, registeredServicesLookupInterval, TimeUnit.SECONDS);
}
@Override
@ -234,136 +152,34 @@ public class SpringCloudRegistry extends FailbackRegistry {
if (isAdminProtocol(url)) {
shutdownServiceNamesLookup();
}
// if (registeredServicesLookupScheduler != null) {
// registeredServicesLookupScheduler.shutdown();
// }
}
@Override
public boolean isAvailable() {
return false;
return !discoveryClient.getServices().isEmpty();
}
private void shutdownServiceNamesLookup() {
if (allServicesLookupScheduler != null) {
allServicesLookupScheduler.shutdown();
if (servicesLookupScheduler != null) {
servicesLookupScheduler.shutdown();
}
}
private Registration createRegistration(String serviceName, URL url) {
return registrationFactory.create(serviceName, url, applicationContext);
}
public static String getServiceName(URL url) {
String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
return getServiceName(url, category);
}
private static String getServiceName(URL url, String category) {
StringBuilder serviceNameBuilder = new StringBuilder(category);
appendIfPresent(serviceNameBuilder, url, Constants.INTERFACE_KEY);
appendIfPresent(serviceNameBuilder, url, Constants.VERSION_KEY);
appendIfPresent(serviceNameBuilder, url, Constants.GROUP_KEY);
return serviceNameBuilder.toString();
}
private static void appendIfPresent(StringBuilder target, URL url, String parameterName) {
String parameterValue = url.getParameter(parameterName);
appendIfPresent(target, parameterValue);
}
private static void appendIfPresent(StringBuilder target, String parameterValue) {
if (StringUtils.hasText(parameterValue)) {
target.append(SERVICE_NAME_SEPARATOR).append(parameterValue);
}
private Registration createRegistration(URL url) {
return registrationFactory.create(url, applicationContext);
}
private void filterServiceNames(List<String> serviceNames, URL url) {
final String[] categories = getCategories(url);
final String targetServiceInterface = url.getServiceInterface();
final String targetVersion = url.getParameter(Constants.VERSION_KEY);
final String targetGroup = url.getParameter(Constants.GROUP_KEY);
private void filterServiceNames(List<String> serviceNames) {
filter(serviceNames, new Filter<String>() {
@Override
public boolean accept(String serviceName) {
// split service name to segments
// (required) segments[0] = category
// (required) segments[1] = serviceInterface
// (required) segments[2] = version
// (optional) segments[3] = group
String[] segments = getServiceSegments(serviceName);
int length = segments.length;
if (length < SERVICE_GROUP_INDEX) { // must present 4 segments or more
return false;
}
String category = getCategory(segments);
if (Arrays.binarySearch(categories, category) > -1) { // no match category
return false;
}
String serviceInterface = getServiceInterface(segments);
if (!WILDCARD.equals(targetServiceInterface) &&
!Objects.equals(targetServiceInterface, serviceInterface)) { // no match service interface
return false;
}
String version = getServiceVersion(segments);
if (!WILDCARD.equals(targetVersion) &&
!Objects.equals(targetVersion, version)) { // no match service version
return false;
}
String group = getServiceGroup(segments);
if (group != null && !WILDCARD.equals(targetGroup)
&& !Objects.equals(targetGroup, group)) { // no match service group
return false;
}
return true;
return dubboRegistryServiceIdHandler.supports(serviceName);
}
});
}
public static String[] getServiceSegments(String serviceName) {
return StringUtils.delimitedListToStringArray(serviceName, SERVICE_NAME_SEPARATOR);
}
public static String getCategory(String[] segments) {
return segments[CATEGORY_INDEX];
}
public static String getServiceInterface(String[] segments) {
return segments[SERVICE_INTERFACE_INDEX];
}
public static String getServiceVersion(String[] segments) {
return segments[SERVICE_VERSION_INDEX];
}
public static String getServiceGroup(String[] segments) {
return segments.length > SERVICE_GROUP_INDEX ? segments[SERVICE_GROUP_INDEX] : null;
}
/**
* Get the categories from {@link URL}
*
* @param url {@link URL}
* @return non-null array
*/
private String[] getCategories(URL url) {
return Constants.ANY_VALUE.equals(url.getServiceInterface()) ?
ALL_SUPPORTED_CATEGORIES : of(Constants.DEFAULT_CATEGORY);
}
private List<String> getAllServiceNames() {
return discoveryClient.getServices();
return new LinkedList<>(discoveryClient.getServices());
}
/**
@ -378,7 +194,7 @@ public class SpringCloudRegistry extends FailbackRegistry {
initAllServicesLookupScheduler(url, listener);
return getServiceNamesForOps(url);
} else {
return doGetServiceNames(url);
return singletonList(dubboRegistryServiceIdHandler.createServiceId(url));
}
}
@ -388,30 +204,14 @@ public class SpringCloudRegistry extends FailbackRegistry {
}
private void initAllServicesLookupScheduler(final URL url, final NotifyListener listener) {
if (allServicesLookupScheduler == null) {
allServicesLookupScheduler = newSingleThreadScheduledExecutor(new NamedThreadFactory("dubbo-all-services-lookup-"));
allServicesLookupScheduler.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
List<String> serviceNames = getAllServiceNames();
filter(serviceNames, new Filter<String>() {
@Override
public boolean accept(String serviceName) {
boolean accepted = false;
for (String category : ALL_SUPPORTED_CATEGORIES) {
String prefix = category + SERVICE_NAME_SEPARATOR;
if (StringUtils.startsWithIgnoreCase(serviceName, prefix)) {
accepted = true;
break;
}
}
return accepted;
}
});
doSubscribe(url, listener, serviceNames);
}
}, ALL_SERVICES_LOOKUP_INTERVAL, ALL_SERVICES_LOOKUP_INTERVAL, TimeUnit.SECONDS);
}
servicesLookupScheduler.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
List<String> serviceNames = getAllServiceNames();
filterServiceNames(serviceNames);
doSubscribe(url, listener, serviceNames);
}
}, allServicesLookupInterval, allServicesLookupInterval, TimeUnit.SECONDS);
}
private void doSubscribe(final URL url, final NotifyListener listener, final List<String> serviceNames) {
@ -421,16 +221,6 @@ public class SpringCloudRegistry extends FailbackRegistry {
}
}
private List<String> doGetServiceNames(URL url) {
String[] categories = getCategories(url);
List<String> serviceNames = new ArrayList<String>(categories.length);
for (String category : categories) {
final String serviceName = getServiceName(url, category);
serviceNames.add(serviceName);
}
return serviceNames;
}
/**
* Notify the Healthy {@link ServiceInstance service instance} to subscriber.
*
@ -487,7 +277,7 @@ public class SpringCloudRegistry extends FailbackRegistry {
*/
private List<String> getServiceNamesForOps(URL url) {
List<String> serviceNames = getAllServiceNames();
filterServiceNames(serviceNames, url);
filterServiceNames(serviceNames);
return serviceNames;
}
@ -508,7 +298,7 @@ public class SpringCloudRegistry extends FailbackRegistry {
/**
* A filter
*/
private interface Filter<T> {
public interface Filter<T> {
/**
* Tests whether or not the specified data should be accepted.

@ -17,9 +17,28 @@
package org.springframework.cloud.alibaba.dubbo.registry;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.NamedThreadFactory;
import org.apache.dubbo.registry.Registry;
import org.apache.dubbo.registry.RegistryFactory;
import org.springframework.context.ApplicationContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.ResolvableType;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import static java.lang.System.getProperty;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static org.springframework.beans.BeanUtils.instantiateClass;
import static org.springframework.core.ResolvableType.forInstance;
import static org.springframework.core.ResolvableType.forType;
import static org.springframework.core.io.support.SpringFactoriesLoader.loadFactoryNames;
import static org.springframework.util.ClassUtils.isPresent;
import static org.springframework.util.ClassUtils.resolveClassName;
/**
* Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose protocol is "spring-cloud"
@ -30,14 +49,122 @@ import org.springframework.context.ApplicationContext;
*/
public class SpringCloudRegistryFactory implements RegistryFactory {
private static ApplicationContext applicationContext;
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 final Logger logger = LoggerFactory.getLogger(getClass());
private final ScheduledExecutorService servicesLookupScheduler;
private ServiceRegistry<Registration> serviceRegistry;
private RegistrationFactory registrationFactory;
private DiscoveryClient discoveryClient;
private volatile boolean initialized = false;
public SpringCloudRegistryFactory() {
servicesLookupScheduler = newSingleThreadScheduledExecutor(
new NamedThreadFactory(SERVICES_LOOKUP_SCHEDULER_THREAD_NAME_PREFIX));
}
protected void init() {
if (initialized || applicationContext == null) {
return;
}
this.serviceRegistry = applicationContext.getBean(ServiceRegistry.class);
this.registrationFactory = buildRegistrationFactory(serviceRegistry, applicationContext.getClassLoader());
this.discoveryClient = applicationContext.getBean(DiscoveryClient.class);
}
@Override
public Registry getRegistry(URL url) {
return new SpringCloudRegistry(url, applicationContext);
init();
return new SpringCloudRegistry(url, serviceRegistry, registrationFactory, discoveryClient,
servicesLookupScheduler, applicationContext);
}
public static void setApplicationContext(ApplicationContext applicationContext) {
public static void setApplicationContext(ConfigurableApplicationContext applicationContext) {
SpringCloudRegistryFactory.applicationContext = applicationContext;
}
private RegistrationFactory buildRegistrationFactory(ServiceRegistry<Registration> serviceRegistry,
ClassLoader classLoader) {
RegistrationFactory registrationFactory = null;
List<String> factoryClassNames = loadFactoryNames(RegistrationFactory.class, classLoader);
ResolvableType serviceRegistryType = forInstance(serviceRegistry);
// Get first generic Class
Class<?> registrationClass = resolveGenericClass(serviceRegistryType, ServiceRegistry.class, 0);
for (String factoryClassName : factoryClassNames) {
if (isPresent(factoryClassName, classLoader)) { // ignore compilation issue
Class<?> factoryClass = resolveClassName(factoryClassName, classLoader);
ResolvableType registrationFactoryType = forType(factoryClass);
Class<?> actualRegistrationClass = resolveGenericClass(registrationFactoryType, RegistrationFactory.class, 0);
if (registrationClass.equals(actualRegistrationClass)) {
registrationFactory = (RegistrationFactory) instantiateClass(registrationFactoryType.getRawClass());
break;
}
}
}
if (registrationFactory == null) {
if (logger.isWarnEnabled()) {
logger.warn("{} implementation can't be resolved by ServiceRegistry[{}]",
registrationClass.getSimpleName(), serviceRegistry.getClass().getName());
}
registrationFactory = new DefaultRegistrationFactory();
} else {
if (logger.isInfoEnabled()) {
logger.info("{} has been resolved by ServiceRegistry[{}]",
registrationFactory.getClass().getName(), serviceRegistry.getClass().getName());
}
}
return registrationFactory;
}
private Class<?> resolveGenericClass(ResolvableType implementedType, Class<?> interfaceClass, int index) {
ResolvableType resolvableType = implementedType;
try {
OUTER:
while (true) {
ResolvableType[] interfaceTypes = resolvableType.getInterfaces();
for (ResolvableType interfaceType : interfaceTypes) {
if (interfaceType.resolve().equals(interfaceClass)) {
resolvableType = interfaceType;
break OUTER;
}
}
ResolvableType superType = resolvableType.getSuperType();
Class<?> superClass = superType.resolve();
if (Object.class.equals(superClass)) {
break;
}
resolvableType = superType;
}
} catch (Throwable e) {
resolvableType = ResolvableType.forType(void.class);
}
return resolvableType.resolveGeneric(index);
}
}

@ -23,7 +23,7 @@ import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.zookeeper.discovery.ZookeeperInstance;
import org.springframework.cloud.zookeeper.serviceregistry.ServiceInstanceRegistration;
import org.springframework.cloud.zookeeper.serviceregistry.ZookeeperRegistration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
/**
* Zookeeper {@link RegistrationFactory}
@ -33,10 +33,7 @@ import org.springframework.context.ApplicationContext;
public class ZookeeperRegistrationFactory extends AbstractRegistrationFactory<ZookeeperRegistration> {
@Override
public ZookeeperRegistration create(String serviceName, URL url, ApplicationContext applicationContext) {
ServiceInstance serviceInstance = createServiceInstance(serviceName, url);
protected ZookeeperRegistration create(URL url, ConfigurableApplicationContext applicationContext, ServiceInstance serviceInstance) {
ZookeeperInstance zookeeperInstance = new ZookeeperInstance(serviceInstance.getInstanceId(),
serviceInstance.getServiceId(), serviceInstance.getMetadata());

@ -0,0 +1,53 @@
/*
* 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.registry.handler;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.registry.Registry;
import org.springframework.context.ConfigurableApplicationContext;
/**
* Dubbo {@link Registry} Spring Cloud Service Id Builder
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public interface DubboRegistryServiceIdHandler {
/**
* Supports the specified id of Spring Cloud Service or not
*
* @param serviceId the specified id of Spring Cloud Service
* @return if supports, return <code>true</code>, or <code>false</code>
*/
boolean supports(String serviceId);
/**
* Creates the id of Spring Cloud Service
*
* @param url The Dubbo's {@link URL}
* @return non-null
*/
String createServiceId(URL url);
/**
* The instance if {@link ConfigurableApplicationContext} .
*
* @return non-null
*/
ConfigurableApplicationContext getContext();
}

@ -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.registry.handler;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.URL;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.StringUtils;
import java.util.Objects;
import static java.lang.System.getProperty;
import static org.apache.dubbo.common.Constants.CONSUMERS_CATEGORY;
import static org.apache.dubbo.common.Constants.PROVIDERS_CATEGORY;
import static org.springframework.util.StringUtils.startsWithIgnoreCase;
/**
* The Standard {@link DubboRegistryServiceIdHandler}
* <p>
* The service ID pattern is "${category}:${interface}:${version}:${group}"
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class StandardDubboRegistryServiceIdHandler implements DubboRegistryServiceIdHandler {
/**
* The separator for service name that could be changed by the Java Property "dubbo.service.name.separator".
*/
protected static final String SERVICE_NAME_SEPARATOR = getProperty("dubbo.service.name.separator", ":");
private final ConfigurableApplicationContext context;
public StandardDubboRegistryServiceIdHandler(ConfigurableApplicationContext context) {
this.context = context;
}
@Override
public boolean supports(String serviceId) {
return startsWithIgnoreCase(serviceId, PROVIDERS_CATEGORY) ||
startsWithIgnoreCase(serviceId, CONSUMERS_CATEGORY);
}
@Override
public String createServiceId(URL url) {
String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
if (!Objects.equals(category, PROVIDERS_CATEGORY) && !Objects.equals(category, CONSUMERS_CATEGORY)) {
category = PROVIDERS_CATEGORY;
}
return createServiceId(url, category);
}
@Override
public ConfigurableApplicationContext getContext() {
return context;
}
/**
* This method maybe override by sub-class.
*
* @param url The Dubbo's {@link URL}
* @param category The category
* @return
*/
protected String createServiceId(URL url, String category) {
StringBuilder serviceNameBuilder = new StringBuilder(category);
appendIfPresent(serviceNameBuilder, url, Constants.INTERFACE_KEY);
appendIfPresent(serviceNameBuilder, url, Constants.VERSION_KEY);
appendIfPresent(serviceNameBuilder, url, Constants.GROUP_KEY);
return serviceNameBuilder.toString();
}
private static void appendIfPresent(StringBuilder target, URL url, String parameterName) {
String parameterValue = url.getParameter(parameterName);
appendIfPresent(target, parameterValue);
}
private static void appendIfPresent(StringBuilder target, String parameterValue) {
if (StringUtils.hasText(parameterValue)) {
target.append(SERVICE_NAME_SEPARATOR).append(parameterValue);
}
}
}

@ -24,7 +24,7 @@ import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.consul.discovery.ConsulDiscoveryProperties;
import org.springframework.cloud.consul.discovery.ConsulServerUtils;
import org.springframework.cloud.consul.serviceregistry.ConsulRegistration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
import java.util.LinkedHashSet;
import java.util.LinkedList;
@ -40,9 +40,7 @@ import java.util.Set;
public class ConsulRegistrationFactory extends AbstractRegistrationFactory<ConsulRegistration> {
@Override
public ConsulRegistration create(String serviceName, URL url, ApplicationContext applicationContext) {
ServiceInstance serviceInstance = createServiceInstance(serviceName, url);
protected ConsulRegistration create(URL url, ConfigurableApplicationContext applicationContext, ServiceInstance serviceInstance) {
Map<String, String> metadata = getMetadata(serviceInstance);
List<String> tags = createTags(metadata);

@ -27,7 +27,7 @@ import org.springframework.cloud.commons.util.InetUtils;
import org.springframework.cloud.netflix.eureka.CloudEurekaInstanceConfig;
import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
/**
* {@link EurekaRegistration} Factory
@ -37,8 +37,7 @@ import org.springframework.context.ApplicationContext;
public class EurekaRegistrationFactory extends AbstractRegistrationFactory<EurekaRegistration> {
@Override
public EurekaRegistration create(String serviceName, URL url, ApplicationContext applicationContext) {
ServiceInstance serviceInstance = createServiceInstance(serviceName, url);
protected EurekaRegistration create(URL url, ConfigurableApplicationContext applicationContext, ServiceInstance serviceInstance) {
CloudEurekaInstanceConfig cloudEurekaInstanceConfig = applicationContext.getBean(CloudEurekaInstanceConfig.class);
ObjectProvider<HealthCheckHandler> healthCheckHandler = applicationContext.getBeanProvider(HealthCheckHandler.class);
EurekaClientConfig eurekaClientConfig = applicationContext.getBean(EurekaClientConfig.class);

@ -16,6 +16,7 @@
*/
package org.springframework.cloud.alibaba.dubbo.service;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.config.spring.ReferenceBean;
import org.apache.dubbo.rpc.service.GenericService;
import org.slf4j.Logger;
@ -30,12 +31,10 @@ import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static com.alibaba.dubbo.common.Constants.DEFAULT_CLUSTER;
import static com.alibaba.dubbo.common.Constants.DEFAULT_PROTOCOL;
import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceGroup;
import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceInterface;
import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceSegments;
import static org.springframework.cloud.alibaba.dubbo.registry.SpringCloudRegistry.getServiceVersion;
import static org.apache.dubbo.common.Constants.DEFAULT_CLUSTER;
import static org.apache.dubbo.common.Constants.DEFAULT_PROTOCOL;
import static org.apache.dubbo.common.Constants.GROUP_KEY;
import static org.apache.dubbo.common.Constants.VERSION_KEY;
/**
* Dubbo {@link GenericService} Factory
@ -66,11 +65,11 @@ public class DubboGenericServiceFactory {
private ReferenceBean<GenericService> build(ServiceRestMetadata serviceRestMetadata,
DubboTransportedMetadata dubboTransportedMetadata) {
String dubboServiceName = serviceRestMetadata.getName();
String[] segments = getServiceSegments(dubboServiceName);
String interfaceName = getServiceInterface(segments);
String version = getServiceVersion(segments);
String group = getServiceGroup(segments);
String urlValue = serviceRestMetadata.getUrl();
URL url = URL.valueOf(urlValue);
String interfaceName = url.getServiceInterface();
String version = url.getParameter(VERSION_KEY);
String group = url.getParameter(GROUP_KEY);
String protocol = dubboTransportedMetadata.getProtocol();
String cluster = dubboTransportedMetadata.getCluster();

@ -9,7 +9,6 @@ org.springframework.context.ApplicationContextInitializer=\
org.springframework.cloud.alibaba.dubbo.context.DubboServiceRegistrationApplicationContextInitializer
org.springframework.cloud.alibaba.dubbo.registry.RegistrationFactory=\
org.springframework.cloud.alibaba.dubbo.registry.DefaultRegistrationFactory,\
org.springframework.cloud.alibaba.dubbo.registry.netflix.eureka.EurekaRegistrationFactory,\
org.springframework.cloud.alibaba.dubbo.registry.apache.zookeeper.ZookeeperRegistrationFactory,\
org.springframework.cloud.alibaba.dubbo.registry.hashicorp.consul.ConsulRegistrationFactory

@ -1,5 +1,8 @@
dubbo:
registry:
address: spring-cloud://nacos
# The Spring Cloud Dubbo's registry extension
address: spring-cloud://localhost
# The traditional Dubbo's registry
# address: zookeeper://127.0.0.1:2181
server:
port: 7070

@ -10,7 +10,10 @@ dubbo:
port: 8081
server: netty
registry:
address: spring-cloud://nacos
# The Spring Cloud Dubbo's registry extension
address: spring-cloud://localhost
# The traditional Dubbo's registry
# address: zookeeper://127.0.0.1:2181
feign:
hystrix:

Loading…
Cancel
Save