1
0
mirror of https://gitee.com/mirrors/Spring-Cloud-Alibaba.git synced 2021-06-26 13:25:11 +08:00

sync code from master

This commit is contained in:
flystar32 2019-04-03 20:06:26 +08:00
parent 04c3d4b3e1
commit b120c13033
42 changed files with 1830 additions and 1390 deletions

View File

@ -14,7 +14,8 @@
<name>Spring Cloud Alibaba Dubbo</name>
<properties>
<dubbo.version>2.7.0</dubbo.version>
<dubbo.version>2.7.1</dubbo.version>
<dubbo-spring-boot.version>2.7.0</dubbo-spring-boot.version>
<spring-cloud-zookeeper.version>2.0.1.RELEASE</spring-cloud-zookeeper.version>
<spring-cloud-consul.version>2.0.2.RELEASE</spring-cloud-consul.version>
<curator.version>4.0.1</curator.version>
@ -178,7 +179,7 @@
<dependency>
<groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-spring-boot-starter</artifactId>
<version>${dubbo.version}</version>
<version>${dubbo-spring-boot.version}</version>
</dependency>
<!-- Netty -->

View File

@ -16,6 +16,11 @@
*/
package org.springframework.cloud.alibaba.dubbo.annotation;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.config.annotation.Reference;
import org.apache.dubbo.rpc.ExporterListener;
import org.apache.dubbo.rpc.Filter;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.client.RestTemplate;
@ -26,6 +31,8 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static org.apache.dubbo.common.Constants.DEFAULT_RETRIES;
/**
* {@link DubboTransported @DubboTransported} annotation indicates that the traditional Spring Cloud Service-to-Service call is transported
* by Dubbo under the hood, there are two main scenarios:
@ -64,4 +71,80 @@ public @interface DubboTransported {
* @return the default cluster is "failover"
*/
String cluster() default "${dubbo.transport.cluster:failover}";
/**
* Whether to reconnect if connection is lost, if not specify, reconnect is enabled by default, and the interval
* for retry connecting is 2000 ms
*
* @see Constants#DEFAULT_RECONNECT_PERIOD
* @see Reference#reconnect()
*/
String reconnect() default "${dubbo.transport.reconnect:2000}";
/**
* Maximum connections service provider can accept, default value is 0 - connection is shared
*
* @see Reference#connections()
*/
int connections() default 0;
/**
* Service invocation retry times
*
* @see Constants#DEFAULT_RETRIES
* @see Reference#retries()
*/
int retries() default DEFAULT_RETRIES;
/**
* Load balance strategy, legal values include: random, roundrobin, leastactive
*
* @see Constants#DEFAULT_LOADBALANCE
* @see Reference#loadbalance()
*/
String loadbalance() default "${dubbo.transport.loadbalance:}";
/**
* Maximum active requests allowed, default value is 0
*
* @see Reference#actives()
*/
int actives() default 0;
/**
* Timeout value for service invocation, default value is 0
*
* @see Reference#timeout()
*/
int timeout() default 0;
/**
* Specify cache implementation for service invocation, legal values include: lru, threadlocal, jcache
*
* @see Reference#cache()
*/
String cache() default "${dubbo.transport.cache:}";
/**
* Filters for service invocation
*
* @see Filter
* @see Reference#filter()
*/
String[] filter() default {};
/**
* Listeners for service exporting and unexporting
*
* @see ExporterListener
* @see Reference#listener()
*/
String[] listener() default {};
/**
* Customized parameter key-value pair, for example: {key1, value1, key2, value2}
*
* @see Reference#parameters()
*/
String[] parameters() default {};
}

View File

@ -28,8 +28,8 @@ import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.cloud.alibaba.dubbo.annotation.DubboTransported;
import org.springframework.cloud.alibaba.dubbo.client.loadbalancer.DubboMetadataInitializerInterceptor;
import org.springframework.cloud.alibaba.dubbo.client.loadbalancer.DubboTransporterInterceptor;
import org.springframework.cloud.alibaba.dubbo.metadata.DubboTransportedMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
import org.springframework.cloud.alibaba.dubbo.metadata.resolver.DubboTransportedAttributesResolver;
import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceExecutionContextFactory;
import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
@ -86,6 +86,7 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration implements BeanClass
private ClassLoader classLoader;
/**
* Adapt the {@link RestTemplate} beans that are annotated {@link LoadBalanced @LoadBalanced} and
* {@link LoadBalanced @LoadBalanced} when Spring Boot application started
@ -94,9 +95,12 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration implements BeanClass
*/
@EventListener(ApplicationStartedEvent.class)
public void adaptRestTemplates() {
DubboTransportedAttributesResolver attributesResolver = new DubboTransportedAttributesResolver(environment);
for (Map.Entry<String, RestTemplate> entry : restTemplates.entrySet()) {
String beanName = entry.getKey();
Map<String, Object> dubboTranslatedAttributes = getDubboTranslatedAttributes(beanName);
Map<String, Object> dubboTranslatedAttributes = getDubboTranslatedAttributes(beanName, attributesResolver);
if (!CollectionUtils.isEmpty(dubboTranslatedAttributes)) {
adaptRestTemplate(entry.getValue(), dubboTranslatedAttributes);
}
@ -107,10 +111,12 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration implements BeanClass
* Gets the annotation attributes {@link RestTemplate} bean being annotated
* {@link DubboTransported @DubboTransported}
*
* @param beanName the bean name of {@link LoadBalanced @LoadBalanced} {@link RestTemplate}
* @param beanName the bean name of {@link LoadBalanced @LoadBalanced} {@link RestTemplate}
* @param attributesResolver {@link DubboTransportedAttributesResolver}
* @return non-null {@link Map}
*/
private Map<String, Object> getDubboTranslatedAttributes(String beanName) {
private Map<String, Object> getDubboTranslatedAttributes(String beanName,
DubboTransportedAttributesResolver attributesResolver) {
Map<String, Object> attributes = Collections.emptyMap();
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
if (beanDefinition instanceof AnnotatedBeanDefinition) {
@ -119,7 +125,7 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration implements BeanClass
attributes = factoryMethodMetadata != null ?
factoryMethodMetadata.getAnnotationAttributes(DUBBO_TRANSPORTED_CLASS_NAME) : Collections.emptyMap();
}
return attributes;
return attributesResolver.resolve(attributes);
}
@ -132,8 +138,6 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration implements BeanClass
*/
private void adaptRestTemplate(RestTemplate restTemplate, Map<String, Object> dubboTranslatedAttributes) {
DubboTransportedMetadata dubboTransportedMetadata = buildDubboTransportedMetadata(dubboTranslatedAttributes);
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>(restTemplate.getInterceptors());
int index = interceptors.indexOf(loadBalancerInterceptor);
@ -144,21 +148,11 @@ public class DubboLoadBalancedRestTemplateAutoConfiguration implements BeanClass
interceptors.add(index++, new DubboMetadataInitializerInterceptor(repository));
interceptors.add(index++, new DubboTransporterInterceptor(repository, restTemplate.getMessageConverters(),
classLoader, dubboTransportedMetadata, serviceFactory, contextFactory));
classLoader, dubboTranslatedAttributes, serviceFactory, contextFactory));
restTemplate.setInterceptors(interceptors);
}
private DubboTransportedMetadata buildDubboTransportedMetadata(Map<String, Object> dubboTranslatedAttributes) {
DubboTransportedMetadata dubboTransportedMetadata = new DubboTransportedMetadata();
String protocol = (String) dubboTranslatedAttributes.get("protocol");
String cluster = (String) dubboTranslatedAttributes.get("cluster");
// resolve placeholders
dubboTransportedMetadata.setProtocol(environment.resolvePlaceholders(protocol));
dubboTransportedMetadata.setCluster(environment.resolvePlaceholders(cluster));
return dubboTransportedMetadata;
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
this.classLoader = classLoader;

View File

@ -16,24 +16,32 @@
*/
package org.springframework.cloud.alibaba.dubbo.autoconfigure;
import feign.Contract;
import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.spring.ServiceBean;
import org.apache.dubbo.config.spring.context.event.ServiceBeanExportedEvent;
import feign.Contract;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.cloud.alibaba.dubbo.metadata.DubboProtocolConfigSupplier;
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
import org.springframework.cloud.alibaba.dubbo.metadata.resolver.DubboServiceBeanMetadataResolver;
import org.springframework.cloud.alibaba.dubbo.metadata.resolver.MetadataResolver;
import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceFactory;
import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataConfigServiceExporter;
import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataConfigServiceProxy;
import org.springframework.cloud.alibaba.dubbo.service.PublishingDubboMetadataConfigService;
import org.springframework.cloud.alibaba.dubbo.util.JSONUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.EventListener;
import java.util.Collection;
import java.util.Iterator;
import static com.alibaba.dubbo.common.Constants.DEFAULT_PROTOCOL;
import java.util.function.Supplier;
/**
* Spring Boot Auto-Configuration class for Dubbo Metadata
@ -41,10 +49,20 @@ import static com.alibaba.dubbo.common.Constants.DEFAULT_PROTOCOL;
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@Configuration
@Import({DubboServiceMetadataRepository.class, PublishingDubboMetadataConfigService.class})
@Import({DubboServiceMetadataRepository.class,
PublishingDubboMetadataConfigService.class,
DubboMetadataConfigServiceExporter.class,
JSONUtils.class})
public class DubboMetadataAutoConfiguration {
public static final String METADATA_PROTOCOL_BEAN_NAME = "metadata";
@Autowired
private PublishingDubboMetadataConfigService dubboMetadataConfigService;
@Autowired
private MetadataResolver metadataResolver;
@Autowired
private DubboMetadataConfigServiceExporter dubboMetadataConfigServiceExporter;
@Bean
@ConditionalOnMissingBean
@ -52,35 +70,9 @@ public class DubboMetadataAutoConfiguration {
return new DubboServiceBeanMetadataResolver(contract);
}
/**
* Build an alias Bean for {@link ProtocolConfig}
*
* @param protocols {@link ProtocolConfig} Beans
* @return {@link ProtocolConfig} bean
*/
@Bean(name = METADATA_PROTOCOL_BEAN_NAME)
public ProtocolConfig protocolConfig(Collection<ProtocolConfig> protocols) {
ProtocolConfig protocolConfig = null;
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) {
protocolConfig = new ProtocolConfig();
protocolConfig.setName(DEFAULT_PROTOCOL);
protocolConfig.setPort(20880);
}
return protocolConfig;
@Bean
public Supplier<ProtocolConfig> dubboProtocolConfigSupplier(ObjectProvider<Collection<ProtocolConfig>> protocols) {
return new DubboProtocolConfigSupplier(protocols);
}
@Bean
@ -88,4 +80,35 @@ public class DubboMetadataAutoConfiguration {
public DubboMetadataConfigServiceProxy dubboMetadataConfigServiceProxy(DubboGenericServiceFactory factory) {
return new DubboMetadataConfigServiceProxy(factory);
}
// Event-Handling
@EventListener(ServiceBeanExportedEvent.class)
public void onServiceBeanExported(ServiceBeanExportedEvent event) {
ServiceBean serviceBean = event.getServiceBean();
publishServiceRestMetadata(serviceBean);
exportDubboMetadataConfigService();
}
@EventListener(ApplicationFailedEvent.class)
public void onApplicationFailed() {
unExportDubboMetadataConfigService();
}
@EventListener(ContextClosedEvent.class)
public void onContextClosed() {
dubboMetadataConfigServiceExporter.unexport();
}
private void publishServiceRestMetadata(ServiceBean serviceBean) {
dubboMetadataConfigService.publishServiceRestMetadata(metadataResolver.resolveServiceRestMetadata(serviceBean));
}
private void exportDubboMetadataConfigService() {
dubboMetadataConfigServiceExporter.export();
}
private void unExportDubboMetadataConfigService() {
dubboMetadataConfigServiceExporter.unexport();
}
}

View File

@ -1,214 +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.autoconfigure;
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.apache.dubbo.config.spring.ServiceBean;
import org.apache.dubbo.config.spring.context.event.ServiceBeanExportedEvent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication;
import org.springframework.boot.context.event.ApplicationFailedEvent;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.cloud.alibaba.dubbo.metadata.resolver.MetadataResolver;
import org.springframework.cloud.alibaba.dubbo.registry.RegistrationFactory;
import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataConfigService;
import org.springframework.cloud.alibaba.dubbo.service.PublishingDubboMetadataConfigService;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.List;
import static org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboMetadataAutoConfiguration.METADATA_PROTOCOL_BEAN_NAME;
/**
* The Auto-Configuration class for Dubbo metadata {@link EventListener event handling}.
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@AutoConfigureAfter(value = {DubboMetadataAutoConfiguration.class})
@Configuration
public class DubboMetadataEventHandlingAutoConfiguration {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private MetadataResolver metadataResolver;
@Autowired
private PublishingDubboMetadataConfigService dubboMetadataConfigService;
@Autowired
private ApplicationConfig applicationConfig;
@Autowired
@Qualifier(METADATA_PROTOCOL_BEAN_NAME)
private ProtocolConfig metadataProtocolConfig;
@Autowired
private ConfigurableApplicationContext context;
@Value("${spring.application.name:application}")
private String currentApplicationName;
/**
* The ServiceConfig of DubboMetadataConfigService to be exported, can be nullable.
*/
private ServiceConfig<DubboMetadataConfigService> serviceConfig;
private ServiceInstance restServiceInstance;
@EventListener(ServiceBeanExportedEvent.class)
public void onServiceBeanExported(ServiceBeanExportedEvent event) {
ServiceBean serviceBean = event.getServiceBean();
publishServiceRestMetadata(serviceBean);
setRestServiceInstance(serviceBean);
}
private void setRestServiceInstance(ServiceBean serviceBean) {
List<URL> urls = serviceBean.getExportedUrls();
urls.stream()
.filter(url -> "rest".equalsIgnoreCase(url.getProtocol()))
.forEach(url -> {
String host = url.getIp();
int port = url.getPort();
if (restServiceInstance == null) {
String instanceId = currentApplicationName + "-" + host + ":" + port;
this.restServiceInstance = new DefaultServiceInstance(instanceId, currentApplicationName,
host, port, false, new HashMap<>());
} else {
if (!host.equals(restServiceInstance.getHost())) {
if (logger.isWarnEnabled()) {
logger.warn("Current application[{}] host is not consistent, expected: {}, actual: {}",
currentApplicationName, restServiceInstance.getHost(), host);
}
}
if (port != restServiceInstance.getPort()) {
if (logger.isWarnEnabled()) {
logger.warn("Current application[{}] port is not consistent, expected: {}, actual: {}",
currentApplicationName, restServiceInstance.getPort(), port);
}
}
}
});
}
@EventListener(ApplicationReadyEvent.class)
public void onApplicationReady() {
exportDubboMetadataConfigService();
}
@EventListener(ApplicationFailedEvent.class)
public void onApplicationFailed() {
unexportDubboMetadataConfigService();
}
@EventListener(ContextClosedEvent.class)
public void onContextClosed() {
unexportDubboMetadataConfigService();
}
@ConditionalOnNotWebApplication
@Bean
public ApplicationRunner applicationRunner() {
return args -> {
if (restServiceInstance == null) {
return;
}
// From RegistrationFactoryProvider
RegistrationFactory registrationFactory = context.getBean(RegistrationFactory.class);
ServiceRegistry<Registration> serviceRegistry = context.getBean(ServiceRegistry.class);
Registration registration = context.getBean(Registration.class);
restServiceInstance.getMetadata().putAll(registration.getMetadata());
serviceRegistry.register(registrationFactory.create(restServiceInstance, context));
};
}
private void publishServiceRestMetadata(ServiceBean serviceBean) {
dubboMetadataConfigService.publishServiceRestMetadata(metadataResolver.resolveServiceRestMetadata(serviceBean));
}
private void exportDubboMetadataConfigService() {
if (serviceConfig != null && serviceConfig.isExported()) {
return;
}
if (StringUtils.isEmpty(dubboMetadataConfigService.getServiceRestMetadata())) {
// If there is no REST metadata, DubboMetadataConfigService will not be exported.
if (logger.isInfoEnabled()) {
logger.info("There is no REST metadata, the Dubbo service[{}] will not be exported.",
dubboMetadataConfigService.getClass().getName());
}
return;
}
serviceConfig = new ServiceConfig<>();
serviceConfig.setInterface(DubboMetadataConfigService.class);
// Use current Spring application name as the Dubbo Service version
serviceConfig.setVersion(currentApplicationName);
serviceConfig.setRef(dubboMetadataConfigService);
serviceConfig.setApplication(applicationConfig);
serviceConfig.setProtocol(metadataProtocolConfig);
serviceConfig.export();
if (logger.isInfoEnabled()) {
logger.info("The Dubbo service[{}] has been exported.", serviceConfig.toString());
}
}
private void unexportDubboMetadataConfigService() {
if (serviceConfig == null || serviceConfig.isUnexported()) {
return;
}
serviceConfig.unexport();
if (logger.isInfoEnabled()) {
logger.info("The Dubbo service[{}] has been unexported.", serviceConfig.toString());
}
}
}

View File

@ -18,18 +18,17 @@ package org.springframework.cloud.alibaba.dubbo.autoconfigure;
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.EnableConfigurationProperties;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.cloud.alibaba.dubbo.registry.RegistrationFactoryProvider;
import org.springframework.cloud.alibaba.dubbo.registry.handler.DubboRegistryServiceIdHandler;
import org.springframework.cloud.alibaba.dubbo.registry.handler.StandardDubboRegistryServiceIdHandler;
import org.springframework.cloud.alibaba.dubbo.env.DubboCloudProperties;
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;
@ -52,6 +51,7 @@ import static org.apache.dubbo.spring.boot.util.DubboUtils.DUBBO_SCAN_PREFIX;
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@Configuration
@EnableConfigurationProperties(DubboCloudProperties.class)
public class DubboServiceAutoConfiguration {
@Bean
@ -71,17 +71,6 @@ public class DubboServiceAutoConfiguration {
static class ParameterResolversConfiguration {
}
@Bean
@ConditionalOnMissingBean
public DubboRegistryServiceIdHandler dubboRegistryServiceIdHandler(ConfigurableApplicationContext context) {
return new StandardDubboRegistryServiceIdHandler(context);
}
@Bean
public RegistrationFactoryProvider registrationFactoryProvider() {
return new RegistrationFactoryProvider();
}
/**
* Bugfix code for an issue : https://github.com/apache/incubator-dubbo-spring-boot-project/issues/459
*
@ -92,6 +81,7 @@ public class DubboServiceAutoConfiguration {
@Bean(name = BASE_PACKAGES_PROPERTY_RESOLVER_BEAN_NAME)
public PropertyResolver dubboScanBasePackagesPropertyResolver(ConfigurableEnvironment environment) {
ConfigurableEnvironment propertyResolver = new AbstractEnvironment() {
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
Map<String, Object> dubboScanProperties = PropertySourcesUtils.getSubProperties(environment, DUBBO_SCAN_PREFIX);
propertySources.addLast(new MapPropertySource("dubboScanProperties", dubboScanProperties));

View File

@ -0,0 +1,193 @@
/*
* 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.autoconfigure;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.config.spring.ServiceBean;
import com.ecwid.consul.v1.agent.model.NewService;
import com.netflix.appinfo.InstanceInfo;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
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;
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaAutoServiceRegistration;
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaRegistration;
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaServiceRegistry;
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;
/**
* Dubbo Service Registration Auto-{@link Configuration}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@Configuration
@Import({DubboServiceRegistrationEventPublishingAspect.class})
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@AutoConfigureAfter(name = {
EUREKA_AUTO_CONFIGURATION_CLASS_NAME,
CONSUL_AUTO_CONFIGURATION_CLASS_NAME,
"org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration"
}, value = {
DubboMetadataAutoConfiguration.class
})
public class DubboServiceRegistrationAutoConfiguration {
public static final String EUREKA_AUTO_CONFIGURATION_CLASS_NAME =
"org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration";
public static final String CONSUL_AUTO_CONFIGURATION_CLASS_NAME =
"org.springframework.cloud.consul.serviceregistry.ConsulAutoServiceRegistrationAutoConfiguration";
public static final String CONSUL_AUTO_REGISTRATION_CLASS_NAME =
"org.springframework.cloud.consul.serviceregistry.ConsulAutoRegistration";
public static final String ZOOKEEPER_AUTO_CONFIGURATION_CLASS_NAME =
"org.springframework.cloud.zookeeper.serviceregistry.ZookeeperAutoServiceRegistrationAutoConfiguration";
private static final Logger logger = LoggerFactory.getLogger(DubboServiceRegistrationAutoConfiguration.class);
@Autowired
private DubboServiceMetadataRepository dubboServiceMetadataRepository;
@Autowired
private JSONUtils jsonUtils;
@EventListener(ServiceInstancePreRegisteredEvent.class)
public void onServiceInstancePreRegistered(ServiceInstancePreRegisteredEvent event) {
Registration registration = event.getSource();
attachURLsIntoMetadata(registration);
}
@Configuration
@ConditionalOnBean(name = EUREKA_AUTO_CONFIGURATION_CLASS_NAME)
@Aspect
class EurekaConfiguration implements SmartInitializingSingleton {
@Autowired
private ObjectProvider<Collection<ServiceBean>> serviceBeans;
@EventListener(ServiceInstancePreRegisteredEvent.class)
public void onServiceInstancePreRegistered(ServiceInstancePreRegisteredEvent event) {
Registration registration = event.getSource();
EurekaRegistration eurekaRegistration = EurekaRegistration.class.cast(registration);
InstanceInfo instanceInfo = eurekaRegistration.getApplicationInfoManager().getInfo();
attachURLsIntoMetadata(instanceInfo.getMetadata());
}
/**
* {@link EurekaServiceRegistry} will register current {@link ServiceInstance service instance} on
* {@link EurekaAutoServiceRegistration#start()} execution(in {@link SmartLifecycle#start() start phase}),
* thus this method must {@link ServiceBean#export() export} all {@link ServiceBean ServiceBeans} in advance.
*/
@Override
public void afterSingletonsInstantiated() {
Collection<ServiceBean> serviceBeans = this.serviceBeans.getIfAvailable();
if (!isEmpty(serviceBeans)) {
serviceBeans.forEach(ServiceBean::export);
}
}
}
@Configuration
@ConditionalOnBean(name = CONSUL_AUTO_CONFIGURATION_CLASS_NAME)
@AutoConfigureOrder
class ConsulConfiguration {
/**
* Handle the pre-registered event of {@link ServiceInstance} for Consul
*
* @param event {@link ServiceInstancePreRegisteredEvent}
*/
@EventListener(ServiceInstancePreRegisteredEvent.class)
public void onServiceInstancePreRegistered(ServiceInstancePreRegisteredEvent event) {
Registration registration = event.getSource();
Class<?> registrationClass = AopUtils.getTargetClass(registration);
String registrationClassName = registrationClass.getName();
if (CONSUL_AUTO_REGISTRATION_CLASS_NAME.equalsIgnoreCase(registrationClassName)) {
ConsulRegistration consulRegistration = (ConsulRegistration) registration;
attachURLsIntoMetadata(consulRegistration);
}
}
private void attachURLsIntoMetadata(ConsulRegistration consulRegistration) {
NewService newService = consulRegistration.getService();
String dubboURLsJson = getDubboURLsJSON();
if (StringUtils.hasText(dubboURLsJson)) {
List<String> tags = newService.getTags();
tags.add(DUBBO_URLS_METADATA_PROPERTY_NAME + "=" + dubboURLsJson);
}
}
}
private void attachURLsIntoMetadata(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);
}
}
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;
}
return jsonUtils.toJSON(urls.stream().map(URL::toFullString).collect(Collectors.toList()));
}
}

View File

@ -0,0 +1,157 @@
/*
* 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.autoconfigure;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.config.spring.ServiceBean;
import org.apache.dubbo.config.spring.context.event.ServiceBeanExportedEvent;
import com.ecwid.consul.v1.agent.model.NewService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.cloud.alibaba.dubbo.registry.event.ServiceInstancePreRegisteredEvent;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.cloud.consul.serviceregistry.ConsulAutoRegistration;
import org.springframework.cloud.consul.serviceregistry.ConsulRegistration;
import org.springframework.cloud.zookeeper.serviceregistry.ServiceInstanceRegistration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.EventListener;
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;
/**
* Dubbo Service Registration Auto-{@link Configuration} for Non-Web application
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@Configuration
@ConditionalOnNotWebApplication
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@AutoConfigureAfter(DubboServiceRegistrationAutoConfiguration.class)
@Aspect
public class DubboServiceRegistrationNonWebApplicationAutoConfiguration {
private static final String REST_PROTOCOL = "rest";
@Autowired
private ServiceRegistry serviceRegistry;
@Autowired
private Registration registration;
private volatile Integer webPort = null;
private volatile boolean registered = false;
@Around("execution(* org.springframework.cloud.client.serviceregistry.Registration.getPort())")
public Object getPort(ProceedingJoinPoint pjp) throws Throwable {
return webPort != null ? webPort : pjp.proceed();
}
@EventListener(ServiceBeanExportedEvent.class)
public void onServiceBeanExported(ServiceBeanExportedEvent event) {
setWebPort(event.getServiceBean());
register();
}
private void register() {
if (registered) {
return;
}
serviceRegistry.register(registration);
registered = true;
}
/**
* Set web port from {@link ServiceBean#getExportedUrls() exported URLs} if "rest" protocol is present.
*
* @param serviceBean {@link ServiceBean}
*/
private void setWebPort(ServiceBean serviceBean) {
if (webPort == null) {
List<URL> urls = serviceBean.getExportedUrls();
urls.stream()
.filter(url -> REST_PROTOCOL.equalsIgnoreCase(url.getProtocol()))
.findFirst()
.ifPresent(url -> {
webPort = url.getPort();
});
}
}
@Configuration
@ConditionalOnBean(name = ZOOKEEPER_AUTO_CONFIGURATION_CLASS_NAME)
class ZookeeperConfiguration implements SmartInitializingSingleton {
@Autowired
private ServiceInstanceRegistration registration;
@EventListener(ServiceInstancePreRegisteredEvent.class)
public void onServiceInstancePreRegistered(ServiceInstancePreRegisteredEvent event) {
registration.setPort(webPort);
}
@Override
public void afterSingletonsInstantiated() {
// invoke getServiceInstance() method to trigger the ServiceInstance building before register
registration.getServiceInstance();
}
}
@Configuration
@ConditionalOnBean(name = CONSUL_AUTO_CONFIGURATION_CLASS_NAME)
class ConsulConfiguration {
/**
* Handle the pre-registered event of {@link ServiceInstance} for Consul
*
* @param event {@link ServiceInstancePreRegisteredEvent}
*/
@EventListener(ServiceInstancePreRegisteredEvent.class)
public void onServiceInstancePreRegistered(ServiceInstancePreRegisteredEvent event) {
Registration registration = event.getSource();
ConsulAutoRegistration consulRegistration = (ConsulAutoRegistration) registration;
setPort(consulRegistration);
}
/**
* Set port on Non-Web Application
*
* @param consulRegistration {@link ConsulRegistration}
*/
private void setPort(ConsulAutoRegistration consulRegistration) {
int port = consulRegistration.getPort();
NewService newService = consulRegistration.getService();
if (newService.getPort() == null) {
newService.setPort(port);
}
}
}
}

View File

@ -18,9 +18,9 @@ package org.springframework.cloud.alibaba.dubbo.client.loadbalancer;
import org.apache.dubbo.rpc.service.GenericException;
import org.apache.dubbo.rpc.service.GenericService;
import org.springframework.cloud.alibaba.dubbo.http.MutableHttpServerRequest;
import org.springframework.cloud.alibaba.dubbo.metadata.DubboServiceMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.DubboTransportedMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.DubboRestServiceMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
@ -57,7 +57,7 @@ public class DubboTransporterInterceptor implements ClientHttpRequestInterceptor
private final DubboClientHttpResponseFactory clientHttpResponseFactory;
private final DubboTransportedMetadata dubboTransportedMetadata;
private final Map<String, Object> dubboTranslatedAttributes;
private final DubboGenericServiceFactory serviceFactory;
@ -68,11 +68,11 @@ public class DubboTransporterInterceptor implements ClientHttpRequestInterceptor
public DubboTransporterInterceptor(DubboServiceMetadataRepository dubboServiceMetadataRepository,
List<HttpMessageConverter<?>> messageConverters,
ClassLoader classLoader,
DubboTransportedMetadata dubboTransportedMetadata,
Map<String, Object> dubboTranslatedAttributes,
DubboGenericServiceFactory serviceFactory,
DubboGenericServiceExecutionContextFactory contextFactory) {
this.repository = dubboServiceMetadataRepository;
this.dubboTransportedMetadata = dubboTransportedMetadata;
this.dubboTranslatedAttributes = dubboTranslatedAttributes;
this.clientHttpResponseFactory = new DubboClientHttpResponseFactory(messageConverters, classLoader);
this.serviceFactory = serviceFactory;
this.contextFactory = contextFactory;
@ -87,16 +87,16 @@ public class DubboTransporterInterceptor implements ClientHttpRequestInterceptor
RequestMetadata clientMetadata = buildRequestMetadata(request);
DubboServiceMetadata dubboServiceMetadata = repository.get(serviceName, clientMetadata);
DubboRestServiceMetadata metadata = repository.get(serviceName, clientMetadata);
if (dubboServiceMetadata == null) {
if (metadata == null) {
// if DubboServiceMetadata is not found, executes next
return execution.execute(request, body);
}
RestMethodMetadata dubboRestMethodMetadata = dubboServiceMetadata.getRestMethodMetadata();
RestMethodMetadata dubboRestMethodMetadata = metadata.getRestMethodMetadata();
GenericService genericService = serviceFactory.create(dubboServiceMetadata, dubboTransportedMetadata);
GenericService genericService = serviceFactory.create(metadata, dubboTranslatedAttributes);
MutableHttpServerRequest httpServerRequest = new MutableHttpServerRequest(request, body);

View File

@ -0,0 +1,81 @@
/*
* 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.env;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import static org.springframework.util.StringUtils.commaDelimitedListToStringArray;
import static org.springframework.util.StringUtils.hasText;
import static org.springframework.util.StringUtils.trimAllWhitespace;
/**
* Dubbo Cloud {@link ConfigurationProperties Properties}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@ConfigurationProperties(prefix = "dubbo.cloud")
public class DubboCloudProperties {
/**
* All services of Dubbo
*/
public static final String ALL_DUBBO_SERVICES = "*";
/**
* The subscribed services, the default value is "*". The multiple value will use comma(",") as the separator.
*
* @see #ALL_DUBBO_SERVICES
*/
private String subscribedServices = ALL_DUBBO_SERVICES;
public String getSubscribedServices() {
return subscribedServices;
}
public void setSubscribedServices(String subscribedServices) {
this.subscribedServices = subscribedServices;
}
/**
* Get the subscribed services as a {@link Set} with configuration order.
*
* @return non-null Read-only {@link Set}
*/
public Set<String> subscribedServices() {
String[] services = commaDelimitedListToStringArray(getSubscribedServices());
if (services.length < 1) {
return Collections.emptySet();
}
Set<String> subscribedServices = new LinkedHashSet<>();
for (String service : services) {
if (hasText(service)) { // filter blank service name
// remove all whitespace
subscribedServices.add(trimAllWhitespace(service));
}
}
return Collections.unmodifiableSet(subscribedServices);
}
}

View File

@ -0,0 +1,207 @@
/*
* 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.env;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import static org.apache.dubbo.common.Constants.DEFAULT_PROTOCOL;
import static org.apache.dubbo.config.spring.util.PropertySourcesUtils.getSubProperties;
/**
* Dubbo {@link WebApplicationType#NONE Non-Web Application} {@link EnvironmentPostProcessor}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class DubboNonWebApplicationEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
private static final String DOT = ".";
/**
* The name of default {@link PropertySource} defined in SpringApplication#configurePropertySources method.
*/
private static final String PROPERTY_SOURCE_NAME = "defaultProperties";
private static final String SERVER_PORT_PROPERTY_NAME = "server.port";
private static final String PORT_PROPERTY_NAME = "port";
private static final String PROTOCOL_PROPERTY_NAME_PREFIX = "dubbo.protocol";
private static final String PROTOCOL_NAME_PROPERTY_NAME_SUFFIX = DOT + "name";
private static final String PROTOCOL_PORT_PROPERTY_NAME_SUFFIX = DOT + PORT_PROPERTY_NAME;
private static final String PROTOCOL_PORT_PROPERTY_NAME = PROTOCOL_PROPERTY_NAME_PREFIX + PROTOCOL_PORT_PROPERTY_NAME_SUFFIX;
private static final String PROTOCOL_NAME_PROPERTY_NAME = PROTOCOL_PROPERTY_NAME_PREFIX + PROTOCOL_NAME_PROPERTY_NAME_SUFFIX;
private static final String PROTOCOLS_PROPERTY_NAME_PREFIX = "dubbo.protocols";
private static final String REST_PROTOCOL = "rest";
private final Logger logger = LoggerFactory.getLogger(DubboNonWebApplicationEnvironmentPostProcessor.class);
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
WebApplicationType webApplicationType = application.getWebApplicationType();
if (!WebApplicationType.NONE.equals(webApplicationType)) { // Just works in Non-Web Application
if (logger.isDebugEnabled()) {
logger.debug("Current application is a Web Application, the process will be ignored.");
}
return;
}
MutablePropertySources propertySources = environment.getPropertySources();
Map<String, Object> defaultProperties = createDefaultProperties(environment);
if (!CollectionUtils.isEmpty(defaultProperties)) {
addOrReplace(propertySources, defaultProperties);
}
}
private Map<String, Object> createDefaultProperties(ConfigurableEnvironment environment) {
Map<String, Object> defaultProperties = new HashMap<String, Object>();
resetServerPort(environment, defaultProperties);
return defaultProperties;
}
/**
* Reset server port property if it's absent, whose value is configured by "dubbbo.protocol.port"
* or "dubbo.protcols.rest.port"
*
* @param environment
* @param defaultProperties
*/
private void resetServerPort(ConfigurableEnvironment environment, Map<String, Object> defaultProperties) {
String serverPort = environment.getProperty(SERVER_PORT_PROPERTY_NAME, environment.getProperty(PORT_PROPERTY_NAME));
if (serverPort != null) {
return;
}
serverPort = getRestPortFromProtocolProperty(environment);
if (serverPort == null) {
serverPort = getRestPortFromProtocolsProperties(environment);
}
setServerPort(environment, serverPort, defaultProperties);
}
private String getRestPortFromProtocolProperty(ConfigurableEnvironment environment) {
String protocol = environment.getProperty(PROTOCOL_NAME_PROPERTY_NAME, DEFAULT_PROTOCOL);
return isRestProtocol(protocol) ?
environment.getProperty(PROTOCOL_PORT_PROPERTY_NAME) :
null;
}
private String getRestPortFromProtocolsProperties(ConfigurableEnvironment environment) {
String restPort = null;
Map<String, Object> subProperties = getSubProperties(environment, PROTOCOLS_PROPERTY_NAME_PREFIX);
Properties properties = new Properties();
properties.putAll(subProperties);
for (String propertyName : properties.stringPropertyNames()) {
if (propertyName.endsWith(PROTOCOL_NAME_PROPERTY_NAME_SUFFIX)) { // protocol name property
String protocol = properties.getProperty(propertyName);
if (isRestProtocol(protocol)) {
String beanName = resolveBeanName(propertyName);
if (StringUtils.hasText(beanName)) {
restPort = properties.getProperty(beanName + PROTOCOL_PORT_PROPERTY_NAME_SUFFIX);
break;
}
}
}
}
return restPort;
}
private String resolveBeanName(String propertyName) {
int index = propertyName.indexOf(DOT);
return index > -1 ? propertyName.substring(0, index) : null;
}
private void setServerPort(ConfigurableEnvironment environment, String serverPort,
Map<String, Object> defaultProperties) {
if (serverPort == null) {
return;
}
defaultProperties.put(SERVER_PORT_PROPERTY_NAME, serverPort);
}
/**
* Copy from BusEnvironmentPostProcessor#addOrReplace(MutablePropertySources, Map)
*
* @param propertySources {@link MutablePropertySources}
* @param map Default Dubbo Properties
*/
private void addOrReplace(MutablePropertySources propertySources,
Map<String, Object> map) {
MapPropertySource target = null;
if (propertySources.contains(PROPERTY_SOURCE_NAME)) {
PropertySource<?> source = propertySources.get(PROPERTY_SOURCE_NAME);
if (source instanceof MapPropertySource) {
target = (MapPropertySource) source;
for (String key : map.keySet()) {
if (!target.containsProperty(key)) {
target.getSource().put(key, map.get(key));
}
}
}
}
if (target == null) {
target = new MapPropertySource(PROPERTY_SOURCE_NAME, map);
}
if (!propertySources.contains(PROPERTY_SOURCE_NAME)) {
propertySources.addLast(target);
}
}
@Override
public int getOrder() { // Keep LOWEST_PRECEDENCE
return LOWEST_PRECEDENCE;
}
private static boolean isRestProtocol(String protocol) {
return REST_PROTOCOL.equalsIgnoreCase(protocol);
}
}

View File

@ -0,0 +1,67 @@
/*
* 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.metadata;
import org.apache.dubbo.config.ProtocolConfig;
import org.springframework.beans.factory.ObjectProvider;
import java.util.Collection;
import java.util.Iterator;
import java.util.function.Supplier;
import static org.apache.dubbo.common.Constants.DEFAULT_PROTOCOL;
/**
* Dubbo's {@link ProtocolConfig} {@link Supplier}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class DubboProtocolConfigSupplier implements Supplier<ProtocolConfig> {
private final ObjectProvider<Collection<ProtocolConfig>> protocols;
public DubboProtocolConfigSupplier(ObjectProvider<Collection<ProtocolConfig>> protocols) {
this.protocols = protocols;
}
@Override
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 (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);
}
return protocolConfig;
}
}

View File

@ -19,17 +19,17 @@ package org.springframework.cloud.alibaba.dubbo.metadata;
import java.util.Objects;
/**
* Dubbo Service Metadata
* Dubbo Rest Service Metadata
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class DubboServiceMetadata {
public class DubboRestServiceMetadata {
private final ServiceRestMetadata serviceRestMetadata;
private final RestMethodMetadata restMethodMetadata;
public DubboServiceMetadata(ServiceRestMetadata serviceRestMetadata, RestMethodMetadata restMethodMetadata) {
public DubboRestServiceMetadata(ServiceRestMetadata serviceRestMetadata, RestMethodMetadata restMethodMetadata) {
this.serviceRestMetadata = serviceRestMetadata;
this.restMethodMetadata = restMethodMetadata;
}
@ -45,8 +45,8 @@ public class DubboServiceMetadata {
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof DubboServiceMetadata)) return false;
DubboServiceMetadata that = (DubboServiceMetadata) o;
if (!(o instanceof DubboRestServiceMetadata)) return false;
DubboRestServiceMetadata that = (DubboRestServiceMetadata) o;
return Objects.equals(serviceRestMetadata, that.serviceRestMetadata) &&
Objects.equals(restMethodMetadata, that.restMethodMetadata);
}

View File

@ -1,61 +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.metadata;
import org.springframework.cloud.alibaba.dubbo.annotation.DubboTransported;
import java.util.Objects;
/**
* {@link DubboTransported @DubboTransported} Metadata
*/
public class DubboTransportedMetadata {
private String protocol;
private String cluster;
public String getProtocol() {
return protocol;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public String getCluster() {
return cluster;
}
public void setCluster(String cluster) {
this.cluster = cluster;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof DubboTransportedMetadata)) return false;
DubboTransportedMetadata that = (DubboTransportedMetadata) o;
return Objects.equals(protocol, that.protocol) &&
Objects.equals(cluster, that.cluster);
}
@Override
public int hashCode() {
return Objects.hash(protocol, cluster);
}
}

View File

@ -20,6 +20,7 @@ import org.springframework.cloud.alibaba.dubbo.annotation.DubboTransported;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
@ -29,29 +30,13 @@ import java.util.Objects;
*/
public class DubboTransportedMethodMetadata {
private final DubboTransportedMetadata dubboTransportedMetadata;
private final MethodMetadata methodMetadata;
public DubboTransportedMethodMetadata(Method method) {
private final Map<String, Object> attributes;
public DubboTransportedMethodMetadata(Method method, Map<String, Object> attributes) {
this.methodMetadata = new MethodMetadata(method);
this.dubboTransportedMetadata = new DubboTransportedMetadata();
}
public String getProtocol() {
return dubboTransportedMetadata.getProtocol();
}
public void setProtocol(String protocol) {
dubboTransportedMetadata.setProtocol(protocol);
}
public String getCluster() {
return dubboTransportedMetadata.getCluster();
}
public void setCluster(String cluster) {
dubboTransportedMetadata.setCluster(cluster);
this.attributes = attributes;
}
public String getName() {
@ -82,25 +67,25 @@ public class DubboTransportedMethodMetadata {
return methodMetadata.getMethod();
}
public DubboTransportedMetadata getDubboTransportedMetadata() {
return dubboTransportedMetadata;
}
public MethodMetadata getMethodMetadata() {
return methodMetadata;
}
public Map<String, Object> getAttributes() {
return attributes;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof DubboTransportedMethodMetadata)) return false;
DubboTransportedMethodMetadata that = (DubboTransportedMethodMetadata) o;
return Objects.equals(dubboTransportedMetadata, that.dubboTransportedMetadata) &&
Objects.equals(methodMetadata, that.methodMetadata);
return Objects.equals(methodMetadata, that.methodMetadata) &&
Objects.equals(attributes, that.attributes);
}
@Override
public int hashCode() {
return Objects.hash(dubboTransportedMetadata, methodMetadata);
return Objects.hash(methodMetadata, attributes);
}
}

View File

@ -16,26 +16,42 @@
*/
package org.springframework.cloud.alibaba.dubbo.metadata.repository;
import org.apache.dubbo.common.URL;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.alibaba.dubbo.env.DubboCloudProperties;
import org.springframework.cloud.alibaba.dubbo.http.matcher.RequestMetadataMatcher;
import org.springframework.cloud.alibaba.dubbo.metadata.DubboServiceMetadata;
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.DubboMetadataConfigService;
import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataConfigServiceProxy;
import org.springframework.cloud.alibaba.dubbo.util.JSONUtils;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.http.HttpRequest;
import org.springframework.stereotype.Repository;
import org.springframework.util.CollectionUtils;
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;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import static org.apache.dubbo.common.Constants.APPLICATION_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;
@ -47,27 +63,113 @@ import static org.springframework.util.CollectionUtils.isEmpty;
@Repository
public class DubboServiceMetadataRepository {
/**
* The property name of Dubbo {@link URL URLs} metadata
*/
public static final String DUBBO_URLS_METADATA_PROPERTY_NAME = "dubbo.urls";
private final Logger logger = LoggerFactory.getLogger(getClass());
private final ObjectMapper objectMapper = new ObjectMapper();
private final Set<URL> registeredURLs = new LinkedHashSet<>();
private final Map<String, String> dubboServiceKeysRepository = new HashMap<>();
/**
* Key is application name
* Value is Map<RequestMetadata, DubboServiceMetadata>
* Value is Map<RequestMetadata, DubboRestServiceMetadata>
*/
private Map<String, Map<RequestMetadataMatcher, DubboServiceMetadata>> repository = newHashMap();
private Map<String, Map<RequestMetadataMatcher, DubboRestServiceMetadata>> dubboRestServiceMetadataRepository = newHashMap();
private Set<String> subscribedServices;
@Autowired
private DubboCloudProperties dubboCloudProperties;
@Autowired
private DubboMetadataConfigServiceProxy dubboMetadataConfigServiceProxy;
@Autowired
private DiscoveryClient discoveryClient;
@Autowired
private JSONUtils jsonUtils;
@Value("${spring.application.name}")
private String currentApplicationName;
@PostConstruct
public void init() {
initSubscribedServices();
initDubboServiceKeysRepository();
retainAvailableSubscribedServices();
initDubboRestServiceMetadataRepository();
}
/**
* Initialize the specified service's Dubbo Service Metadata
* The specified service is subscribe or not
*
* @param serviceName the service name
* @return
*/
public boolean isSubscribedService(String serviceName) {
return subscribedServices.contains(serviceName);
}
/**
* Get the service name by the {@link URL#getServiceKey() service key}
*
* @param url {@link URL}
* @return the service name if found
*/
public String getServiceName(URL url) {
return getServiceName(url.getServiceKey());
}
/**
* Get the service name by the {@link URL#getServiceKey() service key}
*
* @param serviceKey the {@link URL#getServiceKey() service key}
* @return the service name if found
*/
public String getServiceName(String serviceKey) {
return dubboServiceKeysRepository.get(serviceKey);
}
public void registerURL(URL url) {
this.registeredURLs.add(url);
}
public void unregisterURL(URL url) {
this.registeredURLs.remove(url);
}
public Collection<URL> getRegisteredUrls() {
return Collections.unmodifiableSet(registeredURLs);
}
/**
* Build the {@link URL urls} by the specified {@link ServiceInstance}
*
* @param serviceInstance {@link ServiceInstance}
* @return the mutable {@link URL urls}
*/
public List<URL> buildURLs(ServiceInstance serviceInstance) {
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());
}
/**
* Initialize the specified service's {@link ServiceRestMetadata}
*
* @param serviceName the service name
*/
public void initialize(String serviceName) {
if (repository.containsKey(serviceName)) {
if (dubboRestServiceMetadataRepository.containsKey(serviceName)) {
return;
}
@ -81,14 +183,14 @@ public class DubboServiceMetadataRepository {
return;
}
Map<RequestMetadataMatcher, DubboServiceMetadata> metadataMap = getMetadataMap(serviceName);
Map<RequestMetadataMatcher, DubboRestServiceMetadata> metadataMap = getMetadataMap(serviceName);
for (ServiceRestMetadata serviceRestMetadata : serviceRestMetadataSet) {
serviceRestMetadata.getMeta().forEach(restMethodMetadata -> {
RequestMetadata requestMetadata = restMethodMetadata.getRequest();
RequestMetadataMatcher matcher = new RequestMetadataMatcher(requestMetadata);
DubboServiceMetadata metadata = new DubboServiceMetadata(serviceRestMetadata, restMethodMetadata);
DubboRestServiceMetadata metadata = new DubboRestServiceMetadata(serviceRestMetadata, restMethodMetadata);
metadataMap.put(matcher, metadata);
});
}
@ -99,14 +201,14 @@ public class DubboServiceMetadataRepository {
}
/**
* Get a {@link DubboServiceMetadata} by the specified service name if {@link RequestMetadata} matched
* Get a {@link DubboRestServiceMetadata} by the specified service name if {@link RequestMetadata} matched
*
* @param serviceName service name
* @param requestMetadata {@link RequestMetadata} to be matched
* @return {@link DubboServiceMetadata} if matched, or <code>null</code>
* @return {@link DubboRestServiceMetadata} if matched, or <code>null</code>
*/
public DubboServiceMetadata get(String serviceName, RequestMetadata requestMetadata) {
return match(repository, serviceName, requestMetadata);
public DubboRestServiceMetadata get(String serviceName, RequestMetadata requestMetadata) {
return match(dubboRestServiceMetadataRepository, serviceName, requestMetadata);
}
private <T> T match(Map<String, Map<RequestMetadataMatcher, T>> repository, String serviceName,
@ -148,23 +250,22 @@ public class DubboServiceMetadataRepository {
return object;
}
private Map<RequestMetadataMatcher, DubboServiceMetadata> getMetadataMap(String serviceName) {
return getMap(repository, serviceName);
private Map<RequestMetadataMatcher, DubboRestServiceMetadata> getMetadataMap(String serviceName) {
return getMap(dubboRestServiceMetadataRepository, serviceName);
}
private Set<ServiceRestMetadata> getServiceRestMetadataSet(String serviceName) {
DubboMetadataConfigService dubboMetadataConfigService = dubboMetadataConfigServiceProxy.newProxy(serviceName);
String serviceRestMetadataJsonConfig = dubboMetadataConfigService.getServiceRestMetadata();
Set<ServiceRestMetadata> metadata;
Set<ServiceRestMetadata> metadata = Collections.emptySet();
try {
String serviceRestMetadataJsonConfig = dubboMetadataConfigService.getServiceRestMetadata();
metadata = objectMapper.readValue(serviceRestMetadataJsonConfig,
TypeFactory.defaultInstance().constructCollectionType(LinkedHashSet.class, ServiceRestMetadata.class));
} catch (Exception e) {
if (logger.isErrorEnabled()) {
logger.error(e.getMessage(), e);
}
metadata = Collections.emptySet();
}
return metadata;
}
@ -186,4 +287,45 @@ public class DubboServiceMetadataRepository {
return new LinkedHashMap<>();
}
private void initSubscribedServices() {
// If subscribes all services
if (ALL_DUBBO_SERVICES.equalsIgnoreCase(dubboCloudProperties.getSubscribedServices())) {
subscribedServices = new HashSet<>(discoveryClient.getServices());
} else {
subscribedServices = new HashSet<>(dubboCloudProperties.subscribedServices());
}
excludeSelf(subscribedServices);
}
private void excludeSelf(Set<String> subscribedServices) {
subscribedServices.remove(currentApplicationName);
}
private void initDubboServiceKeysRepository() {
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);
});
});
}
private void retainAvailableSubscribedServices() {
// dubboServiceKeysRepository.values() returns the available services(possible duplicated ones)
subscribedServices = new HashSet<>(dubboServiceKeysRepository.values());
}
private void initDubboRestServiceMetadataRepository() {
subscribedServices.forEach(this::initialize);
}
private boolean isNotEmpty(Collection collection) {
return !CollectionUtils.isEmpty(collection);
}
}

View File

@ -0,0 +1,56 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.dubbo.metadata.resolver;
import org.springframework.cloud.alibaba.dubbo.annotation.DubboTransported;
import org.springframework.core.env.PropertyResolver;
import java.util.LinkedHashMap;
import java.util.Map;
import static org.springframework.core.annotation.AnnotationUtils.getAnnotationAttributes;
/**
* {@link DubboTransported} annotation attributes resolver
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class DubboTransportedAttributesResolver {
private final PropertyResolver propertyResolver;
public DubboTransportedAttributesResolver(PropertyResolver propertyResolver) {
this.propertyResolver = propertyResolver;
}
public Map<String, Object> resolve(DubboTransported dubboTransported) {
Map<String, Object> attributes = getAnnotationAttributes(dubboTransported);
return resolve(attributes);
}
public Map<String, Object> resolve(Map<String, Object> attributes) {
Map<String, Object> resolvedAttributes = new LinkedHashMap<>();
for (Map.Entry<String, Object> entry : attributes.entrySet()) {
Object value = entry.getValue();
if (value instanceof String) {
value = propertyResolver.resolvePlaceholders(value.toString());
}
resolvedAttributes.put(entry.getKey(), value);
}
return resolvedAttributes;
}
}

View File

@ -42,12 +42,12 @@ public class DubboTransportedMethodMetadataResolver {
private static final Class<DubboTransported> DUBBO_TRANSPORTED_CLASS = DubboTransported.class;
private final PropertyResolver propertyResolver;
private final DubboTransportedAttributesResolver attributesResolver;
private final Contract contract;
public DubboTransportedMethodMetadataResolver(PropertyResolver propertyResolver, Contract contract) {
this.propertyResolver = propertyResolver;
this.attributesResolver = new DubboTransportedAttributesResolver(propertyResolver);
this.contract = contract;
}
@ -93,12 +93,8 @@ public class DubboTransportedMethodMetadataResolver {
private DubboTransportedMethodMetadata createDubboTransportedMethodMetadata(Method method,
DubboTransported dubboTransported) {
DubboTransportedMethodMetadata methodMetadata = new DubboTransportedMethodMetadata(method);
String protocol = propertyResolver.resolvePlaceholders(dubboTransported.protocol());
String cluster = propertyResolver.resolvePlaceholders(dubboTransported.cluster());
methodMetadata.setProtocol(protocol);
methodMetadata.setCluster(cluster);
return methodMetadata;
Map<String, Object> attributes = attributesResolver.resolve(dubboTransported);
return new DubboTransportedMethodMetadata(method, attributes);
}
private DubboTransported resolveDubboTransported(Method method) {

View File

@ -17,14 +17,18 @@
package org.springframework.cloud.alibaba.dubbo.openfeign;
import org.apache.dubbo.rpc.service.GenericService;
import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata;
import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceExecutionContext;
import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceExecutionContextFactory;
import org.springframework.util.ClassUtils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
import static org.apache.dubbo.common.utils.PojoUtils.realize;
/**
* Dubbo {@link GenericService} for {@link InvocationHandler}
*
@ -38,11 +42,15 @@ public class DubboInvocationHandler implements InvocationHandler {
private final DubboGenericServiceExecutionContextFactory contextFactory;
private final ClassLoader classLoader;
public DubboInvocationHandler(Map<Method, FeignMethodMetadata> feignMethodMetadataMap,
InvocationHandler defaultInvocationHandler,
ClassLoader classLoader,
DubboGenericServiceExecutionContextFactory contextFactory) {
this.feignMethodMetadataMap = feignMethodMetadataMap;
this.defaultInvocationHandler = defaultInvocationHandler;
this.classLoader = classLoader;
this.contextFactory = contextFactory;
}
@ -65,6 +73,15 @@ public class DubboInvocationHandler implements InvocationHandler {
String[] parameterTypes = context.getParameterTypes();
Object[] parameters = context.getParameters();
return dubboGenericService.$invoke(methodName, parameterTypes, parameters);
Object result = dubboGenericService.$invoke(methodName, parameterTypes, parameters);
Class<?> returnType = getReturnType(dubboRestMethodMetadata);
return realize(result, returnType);
}
private Class<?> getReturnType(RestMethodMetadata dubboRestMethodMetadata) {
String returnType = dubboRestMethodMetadata.getReturnType();
return ClassUtils.resolveClassName(returnType, classLoader);
}
}

View File

@ -69,7 +69,7 @@ public class TargeterBeanPostProcessor implements BeanPostProcessor, BeanClassLo
Class<?> targetClass = resolveClassName(TARGETER_CLASS_NAME, classLoader);
if (targetClass.isAssignableFrom(beanClass)) {
return newProxyInstance(classLoader, new Class[]{targetClass},
new TargeterInvocationHandler(bean, environment, dubboServiceMetadataRepository,
new TargeterInvocationHandler(bean, environment, classLoader, dubboServiceMetadataRepository,
dubboGenericServiceFactory, contextFactory));
}
}

View File

@ -17,14 +17,14 @@
package org.springframework.cloud.alibaba.dubbo.openfeign;
import org.apache.dubbo.rpc.service.GenericService;
import feign.Contract;
import feign.Target;
import org.apache.dubbo.rpc.service.GenericService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.alibaba.dubbo.annotation.DubboTransported;
import org.springframework.cloud.alibaba.dubbo.metadata.DubboServiceMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.DubboTransportedMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.DubboRestServiceMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.DubboTransportedMethodMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.MethodMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.RequestMetadata;
@ -57,17 +57,22 @@ class TargeterInvocationHandler implements InvocationHandler {
private final Environment environment;
private final ClassLoader classLoader;
private final DubboServiceMetadataRepository repository;
private final DubboGenericServiceFactory dubboGenericServiceFactory;
private final DubboGenericServiceExecutionContextFactory contextFactory;
TargeterInvocationHandler(Object bean, Environment environment, DubboServiceMetadataRepository repository,
TargeterInvocationHandler(Object bean, Environment environment,
ClassLoader classLoader,
DubboServiceMetadataRepository repository,
DubboGenericServiceFactory dubboGenericServiceFactory,
DubboGenericServiceExecutionContextFactory contextFactory) {
this.bean = bean;
this.environment = environment;
this.classLoader = classLoader;
this.repository = repository;
this.dubboGenericServiceFactory = dubboGenericServiceFactory;
this.contextFactory = contextFactory;
@ -134,7 +139,7 @@ class TargeterInvocationHandler implements InvocationHandler {
InvocationHandler defaultFeignClientInvocationHandler = Proxy.getInvocationHandler(defaultFeignClientProxy);
DubboInvocationHandler dubboInvocationHandler = new DubboInvocationHandler(feignMethodMetadataMap,
defaultFeignClientInvocationHandler, contextFactory);
defaultFeignClientInvocationHandler, classLoader, contextFactory);
return dubboInvocationHandler;
}
@ -147,13 +152,13 @@ class TargeterInvocationHandler implements InvocationHandler {
for (Map.Entry<DubboTransportedMethodMetadata, RestMethodMetadata> entry : feignRestMethodMetadataMap.entrySet()) {
RestMethodMetadata feignRestMethodMetadata = entry.getValue();
RequestMetadata feignRequestMetadata = feignRestMethodMetadata.getRequest();
DubboServiceMetadata dubboServiceMetadata = repository.get(serviceName, feignRequestMetadata);
if (dubboServiceMetadata != null) {
DubboRestServiceMetadata metadata = repository.get(serviceName, feignRequestMetadata);
if (metadata != null) {
DubboTransportedMethodMetadata dubboTransportedMethodMetadata = entry.getKey();
DubboTransportedMetadata dubboTransportedMetadata = dubboTransportedMethodMetadata.getDubboTransportedMetadata();
Map<String, Object> dubboTranslatedAttributes = dubboTransportedMethodMetadata.getAttributes();
Method method = dubboTransportedMethodMetadata.getMethod();
GenericService dubboGenericService = dubboGenericServiceFactory.create(dubboServiceMetadata, dubboTransportedMetadata);
RestMethodMetadata dubboRestMethodMetadata = dubboServiceMetadata.getRestMethodMetadata();
GenericService dubboGenericService = dubboGenericServiceFactory.create(metadata, dubboTranslatedAttributes);
RestMethodMetadata dubboRestMethodMetadata = metadata.getRestMethodMetadata();
MethodMetadata methodMetadata = dubboTransportedMethodMetadata.getMethodMetadata();
FeignMethodMetadata feignMethodMetadata = new FeignMethodMetadata(dubboGenericService,
dubboRestMethodMetadata, feignRestMethodMetadata);

View File

@ -1,68 +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.registry;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.URL;
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;
/**
* Abstract {@link RegistrationFactory} implementation
* <p>
*
* @param <R> The subclass of {@link Registration}
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public abstract class AbstractRegistrationFactory<R extends Registration> implements RegistrationFactory<R> {
public final R create(URL url, ConfigurableApplicationContext applicationContext) {
ServiceInstance serviceInstance = createServiceInstance(url, applicationContext);
return create(serviceInstance, applicationContext);
}
/**
* 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 = url.getIp();
int port = newURL.getParameter(Constants.BIND_PORT_KEY, url.getPort());
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);
}
}

View File

@ -0,0 +1,293 @@
/*
* 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;
import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.URL;
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.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import static java.util.Collections.singleton;
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;
/**
* Abstract Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose protocol is "spring-cloud"
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public abstract class AbstractSpringCloudRegistry extends FailbackRegistry {
/**
* The parameter name of {@link #servicesLookupInterval}
*/
public static final String SERVICES_LOOKUP_INTERVAL_PARAM_NAME = "dubbo.services.lookup.interval";
protected final Logger logger = LoggerFactory.getLogger(getClass());
/**
* The interval in second of lookup service names(only for Dubbo-OPS)
*/
private final long servicesLookupInterval;
private final DiscoveryClient discoveryClient;
protected final ScheduledExecutorService servicesLookupScheduler;
public AbstractSpringCloudRegistry(URL url,
DiscoveryClient discoveryClient,
ScheduledExecutorService servicesLookupScheduler) {
super(url);
this.servicesLookupInterval = url.getParameter(SERVICES_LOOKUP_INTERVAL_PARAM_NAME, 60L);
this.discoveryClient = discoveryClient;
this.servicesLookupScheduler = servicesLookupScheduler;
}
protected boolean shouldRegister(URL url) {
String side = url.getParameter(SIDE_KEY);
boolean should = PROVIDER_SIDE.equals(side); // Only register the Provider.
if (!should) {
if (logger.isDebugEnabled()) {
logger.debug("The URL[{}] should not be registered.", url.toString());
}
}
return should;
}
@Override
public final void doRegister(URL url) {
if (!shouldRegister(url)) {
return;
}
doRegister0(url);
}
/**
* The sub-type should implement to register
*
* @param url {@link URL}
*/
protected abstract void doRegister0(URL url);
@Override
public final void doUnregister(URL url) {
if (!shouldRegister(url)) {
return;
}
doUnregister0(url);
}
/**
* The sub-type should implement to unregister
*
* @param url {@link URL}
*/
protected abstract void doUnregister0(URL url);
@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();
}
}
@Override
public boolean isAvailable() {
return !discoveryClient.getServices().isEmpty();
}
protected void shutdownServiceNamesLookup() {
if (servicesLookupScheduler != null) {
servicesLookupScheduler.shutdown();
}
}
private void filterServiceNames(Collection<String> serviceNames) {
filter(serviceNames, new Filter<String>() {
@Override
public boolean accept(String serviceName) {
return supports(serviceName);
}
});
}
protected abstract boolean supports(String serviceName);
protected final Set<String> getAllServiceNames() {
return new LinkedHashSet<>(discoveryClient.getServices());
}
/**
* 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));
}
}
protected boolean isAdminProtocol(URL url) {
return Constants.ADMIN_PROTOCOL.equals(url.getProtocol());
}
/**
* Get the service names for Dubbo OPS
*
* @param url {@link URL}
* @return non-null
*/
protected Set<String> getServiceNamesForOps(URL url) {
Set<String> serviceNames = getAllServiceNames();
filterServiceNames(serviceNames);
return serviceNames;
}
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);
});
}
protected ScheduledFuture<?> schedule(Runnable runnable) {
return this.servicesLookupScheduler.scheduleAtFixedRate(runnable, servicesLookupInterval,
servicesLookupInterval, TimeUnit.SECONDS);
}
protected List<ServiceInstance> getServiceInstances(String serviceName) {
return discoveryClient.getInstances(serviceName);
}
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);
}
}
}
/**
* 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 void filterHealthyInstances(Collection<ServiceInstance> instances) {
filter(instances, new Filter<ServiceInstance>() {
@Override
public boolean accept(ServiceInstance data) {
// TODO check the details of status
// return serviceRegistry.getStatus(new DubboRegistration(data)) != null;
return true;
}
});
}
protected List<URL> buildURLs(URL consumerURL, Collection<ServiceInstance> serviceInstances) {
if (serviceInstances.isEmpty()) {
return Collections.emptyList();
}
List<URL> urls = new LinkedList<URL>();
for (ServiceInstance serviceInstance : serviceInstances) {
URL url = buildURL(serviceInstance);
if (UrlUtils.isMatch(consumerURL, url)) {
urls.add(url);
}
}
return urls;
}
private URL buildURL(ServiceInstance serviceInstance) {
URL url = new URL(serviceInstance.getMetadata().get(Constants.PROTOCOL_KEY),
serviceInstance.getHost(),
serviceInstance.getPort(),
serviceInstance.getMetadata());
return url;
}
private <T> void filter(Collection<T> collection, Filter<T> filter) {
Iterator<T> iterator = collection.iterator();
while (iterator.hasNext()) {
T data = iterator.next();
if (!filter.accept(data)) { // remove if not accept
iterator.remove();
}
}
}
private static <T> T[] of(T... values) {
return values;
}
/**
* A filter
*/
public interface Filter<T> {
/**
* Tests whether or not the specified data should be accepted.
*
* @param data The data to be tested
* @return <code>true</code> if and only if <code>data</code>
* should be accepted
*/
boolean accept(T data);
}
}

View File

@ -0,0 +1,61 @@
/*
* 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;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.cloud.alibaba.dubbo.registry.event.ServiceInstancePreRegisteredEvent;
import org.springframework.cloud.alibaba.dubbo.registry.event.ServiceInstanceRegisteredEvent;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
/**
* Dubbo Service Registration Event-Publishing Aspect
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
* @see ServiceInstancePreRegisteredEvent
* @see ServiceInstanceRegisteredEvent
*/
@Aspect
public class DubboServiceRegistrationEventPublishingAspect implements ApplicationEventPublisherAware {
/**
* The pointcut expression for {@link ServiceRegistry#register(Registration)}
*/
public static final String REGISTER_POINTCUT_EXPRESSION =
"execution(* org.springframework.cloud.client.serviceregistry.ServiceRegistry.register(*)) && args(registration)";
private ApplicationEventPublisher applicationEventPublisher;
@Before(REGISTER_POINTCUT_EXPRESSION)
public void beforeRegister(Registration registration) {
applicationEventPublisher.publishEvent(new ServiceInstancePreRegisteredEvent(registration));
}
@After(REGISTER_POINTCUT_EXPRESSION)
public void afterRegister(Registration registration) {
applicationEventPublisher.publishEvent(new ServiceInstanceRegisteredEvent(registration));
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
}

View File

@ -1,50 +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.registry;
import org.apache.dubbo.common.URL;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.context.ConfigurableApplicationContext;
/**
* {@link Registration} Factory to createServiceInstance a instance of {@link Registration}
*
* @param <R> The subclass of {@link Registration}
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public interface RegistrationFactory<R extends Registration> {
/**
* Create a instance of {@link R}
*
* @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.
*/
R create(URL url, ConfigurableApplicationContext applicationContext);
/**
* Create a instance of {@link R}
*
* @param serviceInstance {@link ServiceInstance}
* @param applicationContext {@link ConfigurableApplicationContext}
* @return a instance of {@link R}, if null, it indicates the registration will not be executed.
*/
R create(ServiceInstance serviceInstance, ConfigurableApplicationContext applicationContext);
}

View File

@ -1,144 +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.registry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.ResolvableType;
import java.util.List;
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;
/**
* {@link RegistrationFactory} Provider
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class RegistrationFactoryProvider implements FactoryBean<RegistrationFactory>, ApplicationContextAware {
private final Logger logger = LoggerFactory.getLogger(getClass());
private RegistrationFactory registrationFactory;
@Override
public RegistrationFactory getObject() throws BeansException {
return registrationFactory;
}
@Override
public Class<?> getObjectType() {
return RegistrationFactory.class;
}
public boolean isSingleton() {
return true;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ServiceRegistry<Registration> serviceRegistry = applicationContext.getBean(ServiceRegistry.class);
ClassLoader classLoader = applicationContext.getClassLoader();
this.registrationFactory = buildRegistrationFactory(serviceRegistry, classLoader);
}
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);
}
}

View File

@ -16,299 +16,60 @@
*/
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.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.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
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.ConfigurableApplicationContext;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
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;
import java.util.stream.Collectors;
/**
* Dubbo {@link RegistryFactory} uses Spring Cloud Service Registration abstraction, whose protocol is "spring-cloud"
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class SpringCloudRegistry extends FailbackRegistry {
public class SpringCloudRegistry extends AbstractSpringCloudRegistry {
/**
* The parameter name of {@link #allServicesLookupInterval}
*/
public static final String ALL_SERVICES_LOOKUP_INTERVAL_PARAM_NAME = "dubbo.all.services.lookup.interval";
private final DubboServiceMetadataRepository dubboServiceMetadataRepository;
/**
* The parameter name of {@link #registeredServicesLookupInterval}
*/
public static final String REGISTERED_SERVICES_LOOKUP_INTERVAL_PARAM_NAME = "dubbo.registered.services.lookup.interval";
/**
* All supported categories
*/
public static final String[] ALL_SUPPORTED_CATEGORIES = of(
PROVIDERS_CATEGORY,
CONSUMERS_CATEGORY,
ROUTERS_CATEGORY,
CONFIGURATORS_CATEGORY
);
private final Logger logger = LoggerFactory.getLogger(getClass());
/**
* The interval in second of lookup service names(only for Dubbo-OPS)
*/
private final long allServicesLookupInterval;
private final long registeredServicesLookupInterval;
private final ServiceRegistry<Registration> serviceRegistry;
private final RegistrationFactory registrationFactory;
private final DiscoveryClient discoveryClient;
private final DubboRegistryServiceIdHandler dubboRegistryServiceIdHandler;
private final ScheduledExecutorService servicesLookupScheduler;
private final ConfigurableApplicationContext applicationContext;
public SpringCloudRegistry(URL url,
ServiceRegistry<Registration> serviceRegistry,
RegistrationFactory registrationFactory,
DiscoveryClient discoveryClient,
public SpringCloudRegistry(URL url, 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.servicesLookupScheduler = servicesLookupScheduler;
}
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.
DubboServiceMetadataRepository dubboServiceMetadataRepository) {
super(url, discoveryClient, servicesLookupScheduler);
this.dubboServiceMetadataRepository = dubboServiceMetadataRepository;
}
@Override
public void doRegister(URL url) {
final Registration registration = createRegistration(url);
if (shouldRegister(registration)) {
serviceRegistry.register(registration);
}
protected void doRegister0(URL url) {
dubboServiceMetadataRepository.registerURL(url);
}
@Override
public void doUnregister(URL url) {
final Registration registration = createRegistration(url);
if (shouldRegister(registration)) {
this.serviceRegistry.deregister(registration);
}
protected void doUnregister0(URL url) {
dubboServiceMetadataRepository.unregisterURL(url);
}
@Override
public void doSubscribe(URL url, NotifyListener listener) {
List<String> serviceNames = getServiceNames(url, listener);
doSubscribe(url, listener, serviceNames);
this.servicesLookupScheduler.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
doSubscribe(url, listener, serviceNames);
}
}, registeredServicesLookupInterval, registeredServicesLookupInterval, TimeUnit.SECONDS);
protected boolean supports(String serviceName) {
return dubboServiceMetadataRepository.isSubscribedService(serviceName);
}
@Override
public void doUnsubscribe(URL url, NotifyListener listener) {
if (isAdminProtocol(url)) {
shutdownServiceNamesLookup();
}
protected String getServiceName(URL url) {
return dubboServiceMetadataRepository.getServiceName(url);
}
@Override
public boolean isAvailable() {
return !discoveryClient.getServices().isEmpty();
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);
}
private void shutdownServiceNamesLookup() {
if (servicesLookupScheduler != null) {
servicesLookupScheduler.shutdown();
}
}
private Registration createRegistration(URL url) {
return registrationFactory.create(url, applicationContext);
}
private void filterServiceNames(List<String> serviceNames) {
filter(serviceNames, new Filter<String>() {
@Override
public boolean accept(String serviceName) {
return dubboRegistryServiceIdHandler.supports(serviceName);
}
});
}
private List<String> getAllServiceNames() {
return new LinkedList<>(discoveryClient.getServices());
}
/**
* Get the service names from the specified {@link URL url}
*
* @param url {@link URL}
* @param listener {@link NotifyListener}
* @return non-null
*/
private List<String> getServiceNames(URL url, NotifyListener listener) {
if (isAdminProtocol(url)) {
initAllServicesLookupScheduler(url, listener);
return getServiceNamesForOps(url);
} else {
return singletonList(dubboRegistryServiceIdHandler.createServiceId(url));
}
}
private boolean isAdminProtocol(URL url) {
return Constants.ADMIN_PROTOCOL.equals(url.getProtocol());
}
private void initAllServicesLookupScheduler(final URL url, final NotifyListener listener) {
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) {
for (String serviceName : serviceNames) {
List<ServiceInstance> serviceInstances = discoveryClient.getInstances(serviceName);
notifySubscriber(url, listener, serviceInstances);
}
}
/**
* Notify the Healthy {@link ServiceInstance service instance} to subscriber.
*
* @param url {@link URL}
* @param listener {@link NotifyListener}
* @param serviceInstances all {@link ServiceInstance instances}
*/
private void notifySubscriber(URL url, NotifyListener listener, List<ServiceInstance> serviceInstances) {
List<ServiceInstance> healthyInstances = new LinkedList<ServiceInstance>(serviceInstances);
// Healthy Instances
filterHealthyInstances(healthyInstances);
List<URL> urls = buildURLs(url, healthyInstances);
this.notify(url, listener, urls);
}
private void filterHealthyInstances(Collection<ServiceInstance> instances) {
filter(instances, new Filter<ServiceInstance>() {
@Override
public boolean accept(ServiceInstance data) {
// TODO check the details of status
// return serviceRegistry.getStatus(new DubboRegistration(data)) != null;
return true;
}
});
}
private List<URL> buildURLs(URL consumerURL, Collection<ServiceInstance> serviceInstances) {
if (serviceInstances.isEmpty()) {
return Collections.emptyList();
}
List<URL> urls = new LinkedList<URL>();
for (ServiceInstance serviceInstance : serviceInstances) {
URL url = buildURL(serviceInstance);
if (UrlUtils.isMatch(consumerURL, url)) {
urls.add(url);
}
}
return urls;
}
private URL buildURL(ServiceInstance serviceInstance) {
URL url = new URL(serviceInstance.getMetadata().get(Constants.PROTOCOL_KEY),
serviceInstance.getHost(),
serviceInstance.getPort(),
serviceInstance.getMetadata());
return url;
}
/**
* Get the service names for Dubbo OPS
*
* @param url {@link URL}
* @return non-null
*/
private List<String> getServiceNamesForOps(URL url) {
List<String> serviceNames = getAllServiceNames();
filterServiceNames(serviceNames);
return serviceNames;
}
private <T> void filter(Collection<T> collection, Filter<T> filter) {
Iterator<T> iterator = collection.iterator();
while (iterator.hasNext()) {
T data = iterator.next();
if (!filter.accept(data)) { // remove if not accept
iterator.remove();
}
}
}
private static <T> T[] of(T... values) {
return values;
}
/**
* A filter
*/
public interface Filter<T> {
/**
* Tests whether or not the specified data should be accepted.
*
* @param data The data to be tested
* @return <code>true</code> if and only if <code>data</code>
* should be accepted
*/
boolean accept(T data);
}
}

View File

@ -20,11 +20,9 @@ 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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository;
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 java.util.concurrent.ScheduledExecutorService;
@ -46,16 +44,12 @@ public class SpringCloudRegistryFactory implements RegistryFactory {
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 DubboServiceMetadataRepository dubboServiceMetadataRepository;
private volatile boolean initialized = false;
public SpringCloudRegistryFactory() {
@ -67,17 +61,14 @@ public class SpringCloudRegistryFactory implements RegistryFactory {
if (initialized || applicationContext == null) {
return;
}
this.serviceRegistry = applicationContext.getBean(ServiceRegistry.class);
this.registrationFactory = applicationContext.getBean(RegistrationFactory.class);
this.discoveryClient = applicationContext.getBean(DiscoveryClient.class);
this.dubboServiceMetadataRepository = applicationContext.getBean(DubboServiceMetadataRepository.class);
}
@Override
public Registry getRegistry(URL url) {
init();
return new SpringCloudRegistry(url, serviceRegistry, registrationFactory, discoveryClient,
servicesLookupScheduler, applicationContext);
return new SpringCloudRegistry(url, discoveryClient, servicesLookupScheduler, dubboServiceMetadataRepository);
}
public static void setApplicationContext(ConfigurableApplicationContext applicationContext) {

View File

@ -1,49 +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.registry.apache.zookeeper;
import org.springframework.cloud.alibaba.dubbo.registry.AbstractRegistrationFactory;
import org.springframework.cloud.alibaba.dubbo.registry.RegistrationFactory;
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.ConfigurableApplicationContext;
/**
* Zookeeper {@link RegistrationFactory}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class ZookeeperRegistrationFactory extends AbstractRegistrationFactory<ZookeeperRegistration> {
@Override
public ZookeeperRegistration create(ServiceInstance serviceInstance, ConfigurableApplicationContext applicationContext) {
ZookeeperInstance zookeeperInstance = new ZookeeperInstance(serviceInstance.getInstanceId(),
serviceInstance.getServiceId(), serviceInstance.getMetadata());
ZookeeperRegistration registration = ServiceInstanceRegistration
.builder()
.address(serviceInstance.getHost())
.name(serviceInstance.getServiceId())
.payload(zookeeperInstance)
.port(serviceInstance.getPort())
.build();
return registration;
}
}

View File

@ -0,0 +1,39 @@
/*
* 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.event;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import org.springframework.context.ApplicationEvent;
/**
* The before-{@link ServiceRegistry#register(Registration) register} event for {@link ServiceInstance}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class ServiceInstancePreRegisteredEvent extends ApplicationEvent {
public ServiceInstancePreRegisteredEvent(Registration source) {
super(source);
}
@Override
public Registration getSource() {
return (Registration) super.getSource();
}
}

View File

@ -14,21 +14,26 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.alibaba.dubbo.registry;
package org.springframework.cloud.alibaba.dubbo.registry.event;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.cloud.client.serviceregistry.ServiceRegistry;
import java.util.EventObject;
/**
* Default {@link RegistrationFactory}
* The after-{@link ServiceRegistry#register(Registration) register} event for {@link Registration}
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class DefaultRegistrationFactory extends AbstractRegistrationFactory<Registration> {
public class ServiceInstanceRegisteredEvent extends EventObject {
public ServiceInstanceRegisteredEvent(Registration source) {
super(source);
}
@Override
public Registration create(ServiceInstance serviceInstance, ConfigurableApplicationContext applicationContext) {
return new DelegatingRegistration(serviceInstance);
public Registration getSource() {
return (Registration) super.getSource();
}
}

View File

@ -1,53 +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.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();
}

View File

@ -1,96 +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.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);
}
}
}

View File

@ -1,88 +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.registry.hashicorp.consul;
import com.ecwid.consul.v1.agent.model.NewService;
import org.springframework.cloud.alibaba.dubbo.registry.AbstractRegistrationFactory;
import org.springframework.cloud.alibaba.dubbo.registry.RegistrationFactory;
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.ConfigurableApplicationContext;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* {@link ConsulRegistration} {@link RegistrationFactory} implementation
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class ConsulRegistrationFactory extends AbstractRegistrationFactory<ConsulRegistration> {
@Override
public ConsulRegistration create(ServiceInstance serviceInstance, ConfigurableApplicationContext applicationContext) {
Map<String, String> metadata = getMetadata(serviceInstance);
List<String> tags = createTags(metadata);
NewService service = new NewService();
service.setId(serviceInstance.getInstanceId());
service.setName(serviceInstance.getServiceId());
service.setAddress(serviceInstance.getHost());
service.setPort(serviceInstance.getPort());
service.setMeta(metadata);
service.setTags(tags);
ConsulDiscoveryProperties properties = applicationContext.getBean(ConsulDiscoveryProperties.class);
ConsulRegistration registration = new ConsulRegistration(service, properties);
return registration;
}
/**
* @param metadata
* @return
* @see ConsulServerUtils#getMetadata(java.util.List)
*/
private List<String> createTags(Map<String, String> metadata) {
List<String> tags = new LinkedList<>();
for (Map.Entry<String, String> entry : metadata.entrySet()) {
String tag = entry.getKey() + "=" + entry.getValue();
tags.add(tag);
}
return tags;
}
private Map<String, String> getMetadata(ServiceInstance serviceInstance) {
Map<String, String> metadata = serviceInstance.getMetadata();
Set<String> removedKeys = new LinkedHashSet<>();
for (String key : metadata.keySet()) {
if (key.contains(".")) {
removedKeys.add(key);
}
}
for (String removedKey : removedKeys) {
metadata.remove(removedKey);
}
return metadata;
}
}

View File

@ -1,59 +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.registry.netflix.eureka;
import com.netflix.appinfo.HealthCheckHandler;
import com.netflix.discovery.EurekaClientConfig;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.alibaba.dubbo.registry.AbstractRegistrationFactory;
import org.springframework.cloud.client.ServiceInstance;
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.ConfigurableApplicationContext;
/**
* {@link EurekaRegistration} Factory
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class EurekaRegistrationFactory extends AbstractRegistrationFactory<EurekaRegistration> {
@Override
public EurekaRegistration create(ServiceInstance serviceInstance, ConfigurableApplicationContext applicationContext) {
CloudEurekaInstanceConfig cloudEurekaInstanceConfig = applicationContext.getBean(CloudEurekaInstanceConfig.class);
ObjectProvider<HealthCheckHandler> healthCheckHandler = applicationContext.getBeanProvider(HealthCheckHandler.class);
EurekaClientConfig eurekaClientConfig = applicationContext.getBean(EurekaClientConfig.class);
InetUtils inetUtils = applicationContext.getBean(InetUtils.class);
EurekaInstanceConfigBean eurekaInstanceConfigBean = new EurekaInstanceConfigBean(inetUtils);
BeanUtils.copyProperties(cloudEurekaInstanceConfig, eurekaInstanceConfigBean);
String serviceId = serviceInstance.getServiceId();
eurekaInstanceConfigBean.setInstanceId(serviceInstance.getInstanceId());
eurekaInstanceConfigBean.setVirtualHostName(serviceId);
eurekaInstanceConfigBean.setSecureVirtualHostName(serviceId);
eurekaInstanceConfigBean.setAppname(serviceId);
eurekaInstanceConfigBean.setHostname(serviceInstance.getHost());
eurekaInstanceConfigBean.setMetadataMap(serviceInstance.getMetadata());
return EurekaRegistration.builder(eurekaInstanceConfigBean)
.with(healthCheckHandler)
.with(eurekaClientConfig, applicationContext)
.build();
}
}

View File

@ -17,24 +17,31 @@
package org.springframework.cloud.alibaba.dubbo.service;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.config.spring.ReferenceBean;
import org.apache.dubbo.rpc.service.GenericService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.alibaba.dubbo.metadata.DubboServiceMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.DubboTransportedMetadata;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.propertyeditors.StringTrimmerEditor;
import org.springframework.cloud.alibaba.dubbo.metadata.DubboRestServiceMetadata;
import org.springframework.cloud.alibaba.dubbo.metadata.ServiceRestMetadata;
import org.springframework.util.StringUtils;
import org.springframework.validation.DataBinder;
import javax.annotation.PreDestroy;
import java.beans.PropertyEditorSupport;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import static org.apache.dubbo.common.Constants.DEFAULT_CLUSTER;
import static org.apache.dubbo.common.Constants.DEFAULT_PROTOCOL;
import static java.util.Collections.emptyMap;
import static org.apache.dubbo.common.Constants.GROUP_KEY;
import static org.apache.dubbo.common.Constants.VERSION_KEY;
import static org.springframework.util.StringUtils.commaDelimitedListToStringArray;
/**
* Dubbo {@link GenericService} Factory
@ -47,39 +54,36 @@ public class DubboGenericServiceFactory {
private final ConcurrentMap<Integer, ReferenceBean<GenericService>> cache = new ConcurrentHashMap<>();
public GenericService create(DubboServiceMetadata dubboServiceMetadata,
DubboTransportedMetadata dubboTransportedMetadata) {
public GenericService create(DubboRestServiceMetadata dubboServiceMetadata,
Map<String, Object> dubboTranslatedAttributes) {
ReferenceBean<GenericService> referenceBean = build(dubboServiceMetadata.getServiceRestMetadata(), dubboTransportedMetadata);
ReferenceBean<GenericService> referenceBean = build(dubboServiceMetadata.getServiceRestMetadata(), dubboTranslatedAttributes);
return referenceBean == null ? null : referenceBean.get();
}
public GenericService create(String serviceName, Class<?> serviceClass) {
String interfaceName = serviceClass.getName();
ReferenceBean<GenericService> referenceBean = build(interfaceName, serviceName, null,
DEFAULT_PROTOCOL, DEFAULT_CLUSTER);
ReferenceBean<GenericService> referenceBean = build(interfaceName, serviceName, null, emptyMap());
return referenceBean.get();
}
private ReferenceBean<GenericService> build(ServiceRestMetadata serviceRestMetadata,
DubboTransportedMetadata dubboTransportedMetadata) {
Map<String, Object> dubboTranslatedAttributes) {
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();
String group = url.getParameter(GROUP_KEY);
return build(interfaceName, version, group, protocol, cluster);
return build(interfaceName, version, group, dubboTranslatedAttributes);
}
private ReferenceBean<GenericService> build(String interfaceName, String version, String group, String protocol,
String cluster) {
private ReferenceBean<GenericService> build(String interfaceName, String version, String group,
Map<String, Object> dubboTranslatedAttributes) {
Integer key = Objects.hash(interfaceName, version, group, protocol, cluster);
Integer key = Objects.hash(interfaceName, version, group, dubboTranslatedAttributes);
ReferenceBean<GenericService> referenceBean = cache.get(key);
@ -89,13 +93,38 @@ public class DubboGenericServiceFactory {
referenceBean.setInterface(interfaceName);
referenceBean.setVersion(version);
referenceBean.setGroup(group);
referenceBean.setProtocol(protocol);
referenceBean.setCluster(cluster);
bindReferenceBean(referenceBean, dubboTranslatedAttributes);
}
return referenceBean;
}
private void bindReferenceBean(ReferenceBean<GenericService> referenceBean, Map<String, Object> dubboTranslatedAttributes) {
DataBinder dataBinder = new DataBinder(referenceBean);
// Register CustomEditors for special fields
dataBinder.registerCustomEditor(String.class, "filter", new StringTrimmerEditor(true));
dataBinder.registerCustomEditor(String.class, "listener", new StringTrimmerEditor(true));
dataBinder.registerCustomEditor(Map.class, "parameters", new PropertyEditorSupport() {
public void setAsText(String text) throws java.lang.IllegalArgumentException {
// Trim all whitespace
String content = StringUtils.trimAllWhitespace(text);
if (!StringUtils.hasText(content)) { // No content , ignore directly
return;
}
// replace "=" to ","
content = StringUtils.replace(content, "=", ",");
// replace ":" to ","
content = StringUtils.replace(content, ":", ",");
// String[] to Map
Map<String, String> parameters = CollectionUtils.toStringMap(commaDelimitedListToStringArray(content));
setValue(parameters);
}
});
dataBinder.bind(new MutablePropertyValues(dubboTranslatedAttributes));
}
@PreDestroy
public void destroy() {
destroyReferenceBeans();

View File

@ -0,0 +1,109 @@
/*
* 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.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.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.function.Supplier;
/**
* {@link DubboMetadataConfigService} exporter
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
@Component
public class DubboMetadataConfigServiceExporter {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private ApplicationConfig applicationConfig;
@Autowired
private PublishingDubboMetadataConfigService dubboMetadataConfigService;
@Autowired
private Supplier<ProtocolConfig> protocolConfigSupplier;
@Value("${spring.application.name:application}")
private String currentApplicationName;
/**
* The ServiceConfig of DubboMetadataConfigService to be exported, can be nullable.
*/
private ServiceConfig<DubboMetadataConfigService> serviceConfig;
/**
* export {@link DubboMetadataConfigService} as Dubbo service
*/
public void export() {
if (serviceConfig != null && serviceConfig.isExported()) {
return;
}
if (StringUtils.isEmpty(dubboMetadataConfigService.getServiceRestMetadata())) {
// If there is no REST metadata, DubboMetadataConfigService will not be exported.
if (logger.isInfoEnabled()) {
logger.info("There is no REST metadata, the Dubbo service[{}] will not be exported.",
dubboMetadataConfigService.getClass().getName());
}
return;
}
serviceConfig = new ServiceConfig<>();
serviceConfig.setInterface(DubboMetadataConfigService.class);
// Use current Spring application name as the Dubbo Service version
serviceConfig.setVersion(currentApplicationName);
serviceConfig.setRef(dubboMetadataConfigService);
serviceConfig.setApplication(applicationConfig);
serviceConfig.setProtocol(protocolConfigSupplier.get());
serviceConfig.export();
if (logger.isInfoEnabled()) {
logger.info("The Dubbo service[{}] has been exported.", serviceConfig.toString());
}
}
/**
* unexport {@link DubboMetadataConfigService}
*/
public void unexport() {
if (serviceConfig == null || serviceConfig.isUnexported()) {
return;
}
serviceConfig.unexport();
if (logger.isInfoEnabled()) {
logger.info("The Dubbo service[{}] has been unexported.", serviceConfig.toString());
}
}
}

View File

@ -16,13 +16,11 @@
*/
package org.springframework.cloud.alibaba.dubbo.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
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 javax.annotation.PostConstruct;
import java.util.LinkedHashSet;
import java.util.Set;
@ -41,12 +39,8 @@ public class PublishingDubboMetadataConfigService implements DubboMetadataConfig
*/
private final Set<ServiceRestMetadata> serviceRestMetadata = new LinkedHashSet<>();
private final ObjectMapper objectMapper = new ObjectMapper();
@PostConstruct
public void init() {
this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
}
@Autowired
private JSONUtils jsonUtils;
/**
* Publish the {@link Set} of {@link ServiceRestMetadata}
@ -64,12 +58,8 @@ public class PublishingDubboMetadataConfigService implements DubboMetadataConfig
@Override
public String getServiceRestMetadata() {
String serviceRestMetadataJsonConfig = null;
try {
if (!isEmpty(serviceRestMetadata)) {
serviceRestMetadataJsonConfig = objectMapper.writeValueAsString(serviceRestMetadata);
}
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
if (!isEmpty(serviceRestMetadata)) {
serviceRestMetadataJsonConfig = jsonUtils.toJSON(serviceRestMetadata);
}
return serviceRestMetadataJsonConfig;
}

View File

@ -0,0 +1,72 @@
/*
* 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.util;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
/**
* JSON Utilities class
*
* @author <a href="mailto:mercyblitz@gmail.com">Mercy</a>
*/
public class JSONUtils {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final ObjectMapper objectMapper = new ObjectMapper();
@PostConstruct
public void init() {
this.objectMapper.enable(SerializationFeature.INDENT_OUTPUT);
}
public String toJSON(Object object) {
String jsonContent = null;
try {
jsonContent = objectMapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
if (logger.isErrorEnabled()) {
logger.error(e.getMessage(), e);
}
}
return jsonContent;
}
public List<String> toList(String json) {
List<String> list = Collections.emptyList();
try {
if (StringUtils.hasText(json)) {
list = objectMapper.readValue(json, List.class);
}
} catch (IOException e) {
if (logger.isErrorEnabled()) {
logger.error(e.getMessage(), e);
}
}
return list;
}
}

View File

@ -1,14 +1,15 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboMetadataAutoConfiguration,\
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboOpenFeignAutoConfiguration,\
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboMetadataEventHandlingAutoConfiguration,\
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboLoadBalancedRestTemplateAutoConfiguration,\
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboServiceAutoConfiguration
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboMetadataAutoConfiguration,\
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboOpenFeignAutoConfiguration,\
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboServiceRegistrationAutoConfiguration,\
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboServiceRegistrationNonWebApplicationAutoConfiguration,\
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboLoadBalancedRestTemplateAutoConfiguration,\
org.springframework.cloud.alibaba.dubbo.autoconfigure.DubboServiceAutoConfiguration
org.springframework.context.ApplicationContextInitializer=\
org.springframework.cloud.alibaba.dubbo.context.DubboServiceRegistrationApplicationContextInitializer
org.springframework.cloud.alibaba.dubbo.context.DubboServiceRegistrationApplicationContextInitializer
org.springframework.cloud.alibaba.dubbo.registry.RegistrationFactory=\
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
org.springframework.boot.env.EnvironmentPostProcessor=\
org.springframework.cloud.alibaba.dubbo.env.DubboNonWebApplicationEnvironmentPostProcessor

View File

@ -16,12 +16,10 @@
package org.springframework.cloud.alibaba.nacos;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnNotWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.alibaba.nacos.discovery.NacosDiscoveryClientAutoConfiguration;
@ -43,17 +41,10 @@ import org.springframework.context.annotation.Configuration;
@EnableConfigurationProperties
@ConditionalOnNacosDiscoveryEnabled
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
@AutoConfigureBefore(NacosDiscoveryClientAutoConfiguration.class)
@AutoConfigureAfter({ AutoServiceRegistrationConfiguration.class,
AutoServiceRegistrationAutoConfiguration.class })
public class NacosDiscoveryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public NacosDiscoveryProperties nacosProperties() {
return new NacosDiscoveryProperties();
}
@Bean
public NacosServiceRegistry nacosServiceRegistry(
NacosDiscoveryProperties nacosDiscoveryProperties) {
@ -77,26 +68,4 @@ public class NacosDiscoveryAutoConfiguration {
return new NacosAutoServiceRegistration(registry,
autoServiceRegistrationProperties, registration);
}
@Bean
@ConditionalOnBean(NacosAutoServiceRegistration.class) // NacosAutoServiceRegistration
// should be present
@ConditionalOnNotWebApplication // Not Web Application
public ApplicationRunner applicationRunner(
NacosAutoServiceRegistration nacosAutoServiceRegistration) {
return args -> {
// WebServerInitializedEvent should not be multicast in Non-Web environment.
// Whatever, NacosAutoServiceRegistration must be checked it's running or not.
if (!nacosAutoServiceRegistration.isRunning()) { // If it's not running, let
// it start.
// FIXME: Please make sure "spring.cloud.nacos.discovery.port" must be
// configured on an available port,
// or the startup or Nacos health check will be failed.
nacosAutoServiceRegistration.start();
// NacosAutoServiceRegistration will be stopped after its destroy() method
// is invoked.
// @PreDestroy destroy() -> stop()
}
};
}
}

View File

@ -33,11 +33,16 @@ import org.springframework.context.annotation.Configuration;
*/
@Configuration
@ConditionalOnNacosDiscoveryEnabled
@EnableConfigurationProperties
@AutoConfigureBefore({ SimpleDiscoveryClientAutoConfiguration.class,
CommonsClientAutoConfiguration.class })
public class NacosDiscoveryClientAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public NacosDiscoveryProperties nacosProperties() {
return new NacosDiscoveryProperties();
}
@Bean
public DiscoveryClient nacosDiscoveryClient(
NacosDiscoveryProperties discoveryProperties) {