diff --git a/spring-cloud-alibaba-dubbo/pom.xml b/spring-cloud-alibaba-dubbo/pom.xml index f01815fe..d707cfb5 100644 --- a/spring-cloud-alibaba-dubbo/pom.xml +++ b/spring-cloud-alibaba-dubbo/pom.xml @@ -14,7 +14,8 @@ Spring Cloud Alibaba Dubbo - 2.7.0 + 2.7.1 + 2.7.0 2.0.1.RELEASE 2.0.2.RELEASE 4.0.1 @@ -178,7 +179,7 @@ org.apache.dubbo dubbo-spring-boot-starter - ${dubbo.version} + ${dubbo-spring-boot.version} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/annotation/DubboTransported.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/annotation/DubboTransported.java index 2714923d..f5cfe644 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/annotation/DubboTransported.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/annotation/DubboTransported.java @@ -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 {}; } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboLoadBalancedRestTemplateAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboLoadBalancedRestTemplateAutoConfiguration.java index e97acfca..a6afc5a9 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboLoadBalancedRestTemplateAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboLoadBalancedRestTemplateAutoConfiguration.java @@ -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 entry : restTemplates.entrySet()) { String beanName = entry.getKey(); - Map dubboTranslatedAttributes = getDubboTranslatedAttributes(beanName); + Map 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 getDubboTranslatedAttributes(String beanName) { + private Map getDubboTranslatedAttributes(String beanName, + DubboTransportedAttributesResolver attributesResolver) { Map 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 dubboTranslatedAttributes) { - DubboTransportedMetadata dubboTransportedMetadata = buildDubboTransportedMetadata(dubboTranslatedAttributes); - List 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 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; diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboMetadataAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboMetadataAutoConfiguration.java index 9464e211..94184b4b 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboMetadataAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboMetadataAutoConfiguration.java @@ -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 Mercy */ @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 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 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 dubboProtocolConfigSupplier(ObjectProvider> 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(); + } } \ No newline at end of file diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboMetadataEventHandlingAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboMetadataEventHandlingAutoConfiguration.java deleted file mode 100644 index 049a2bcf..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboMetadataEventHandlingAutoConfiguration.java +++ /dev/null @@ -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 Mercy - */ -@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 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 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 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()); - } - } -} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceAutoConfiguration.java index 88da964d..02f190de 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceAutoConfiguration.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceAutoConfiguration.java @@ -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 Mercy */ @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 dubboScanProperties = PropertySourcesUtils.getSubProperties(environment, DUBBO_SCAN_PREFIX); propertySources.addLast(new MapPropertySource("dubboScanProperties", dubboScanProperties)); diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceRegistrationAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceRegistrationAutoConfiguration.java new file mode 100644 index 00000000..4ca3246e --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceRegistrationAutoConfiguration.java @@ -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 Mercy + */ +@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> 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 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 tags = newService.getTags(); + tags.add(DUBBO_URLS_METADATA_PROPERTY_NAME + "=" + dubboURLsJson); + } + } + } + + private void attachURLsIntoMetadata(Registration registration) { + if (registration == null) { + return; + } + synchronized (registration) { + Map metadata = registration.getMetadata(); + attachURLsIntoMetadata(metadata); + } + } + + private void attachURLsIntoMetadata(Map metadata) { + String dubboURLsJson = getDubboURLsJSON(); + if (StringUtils.hasText(dubboURLsJson)) { + metadata.put(DUBBO_URLS_METADATA_PROPERTY_NAME, dubboURLsJson); + } + } + + private String getDubboURLsJSON() { + Collection 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())); + } +} \ No newline at end of file diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceRegistrationNonWebApplicationAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceRegistrationNonWebApplicationAutoConfiguration.java new file mode 100644 index 00000000..15a5190b --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboServiceRegistrationNonWebApplicationAutoConfiguration.java @@ -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 Mercy + */ +@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 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); + } + } + } + +} \ No newline at end of file diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboTransporterInterceptor.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboTransporterInterceptor.java index 0d2864fc..4860a3c3 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboTransporterInterceptor.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/client/loadbalancer/DubboTransporterInterceptor.java @@ -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 dubboTranslatedAttributes; private final DubboGenericServiceFactory serviceFactory; @@ -68,11 +68,11 @@ public class DubboTransporterInterceptor implements ClientHttpRequestInterceptor public DubboTransporterInterceptor(DubboServiceMetadataRepository dubboServiceMetadataRepository, List> messageConverters, ClassLoader classLoader, - DubboTransportedMetadata dubboTransportedMetadata, + Map 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); diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/env/DubboCloudProperties.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/env/DubboCloudProperties.java new file mode 100644 index 00000000..bb4d3864 --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/env/DubboCloudProperties.java @@ -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 Mercy + */ +@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 subscribedServices() { + + String[] services = commaDelimitedListToStringArray(getSubscribedServices()); + + if (services.length < 1) { + return Collections.emptySet(); + } + + Set 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); + } +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/env/DubboNonWebApplicationEnvironmentPostProcessor.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/env/DubboNonWebApplicationEnvironmentPostProcessor.java new file mode 100644 index 00000000..5d63860e --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/env/DubboNonWebApplicationEnvironmentPostProcessor.java @@ -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 Mercy + */ +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 defaultProperties = createDefaultProperties(environment); + if (!CollectionUtils.isEmpty(defaultProperties)) { + addOrReplace(propertySources, defaultProperties); + } + } + + private Map createDefaultProperties(ConfigurableEnvironment environment) { + Map defaultProperties = new HashMap(); + 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 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 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 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 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); + } +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboProtocolConfigSupplier.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboProtocolConfigSupplier.java new file mode 100644 index 00000000..d5d99430 --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboProtocolConfigSupplier.java @@ -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 Mercy + */ +public class DubboProtocolConfigSupplier implements Supplier { + + private final ObjectProvider> protocols; + + public DubboProtocolConfigSupplier(ObjectProvider> protocols) { + this.protocols = protocols; + } + + @Override + public ProtocolConfig get() { + ProtocolConfig protocolConfig = null; + Collection 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 iterator = protocols.iterator(); + protocolConfig = iterator.hasNext() ? iterator.next() : null; + } + + if (protocolConfig == null) { + protocolConfig = new ProtocolConfig(); + protocolConfig.setName(DEFAULT_PROTOCOL); + protocolConfig.setPort(20880); + } + + return protocolConfig; + } +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboServiceMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboRestServiceMetadata.java similarity index 84% rename from spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboServiceMetadata.java rename to spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboRestServiceMetadata.java index f245df09..5c5ef3ad 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboServiceMetadata.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboRestServiceMetadata.java @@ -19,17 +19,17 @@ package org.springframework.cloud.alibaba.dubbo.metadata; import java.util.Objects; /** - * Dubbo Service Metadata + * Dubbo Rest Service Metadata * * @author Mercy */ -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); } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboTransportedMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboTransportedMetadata.java deleted file mode 100644 index 2811bdaf..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboTransportedMetadata.java +++ /dev/null @@ -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); - } -} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboTransportedMethodMetadata.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboTransportedMethodMetadata.java index ade65aa9..6e7ab9ac 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboTransportedMethodMetadata.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboTransportedMethodMetadata.java @@ -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 attributes; + + public DubboTransportedMethodMetadata(Method method, Map 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 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); } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/repository/DubboServiceMetadataRepository.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/repository/DubboServiceMetadataRepository.java index e8d21a31..53f86893 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/repository/DubboServiceMetadataRepository.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/repository/DubboServiceMetadataRepository.java @@ -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 registeredURLs = new LinkedHashSet<>(); + + private final Map dubboServiceKeysRepository = new HashMap<>(); + /** * Key is application name - * Value is Map + * Value is Map */ - private Map> repository = newHashMap(); + private Map> dubboRestServiceMetadataRepository = newHashMap(); + + private Set 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 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 buildURLs(ServiceInstance serviceInstance) { + Map metadata = serviceInstance.getMetadata(); + String dubboURLsJSON = metadata.get(DUBBO_URLS_METADATA_PROPERTY_NAME); + List 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 metadataMap = getMetadataMap(serviceName); + Map 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 null + * @return {@link DubboRestServiceMetadata} if matched, or null */ - 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 match(Map> repository, String serviceName, @@ -148,23 +250,22 @@ public class DubboServiceMetadataRepository { return object; } - private Map getMetadataMap(String serviceName) { - return getMap(repository, serviceName); + private Map getMetadataMap(String serviceName) { + return getMap(dubboRestServiceMetadataRepository, serviceName); } private Set getServiceRestMetadataSet(String serviceName) { DubboMetadataConfigService dubboMetadataConfigService = dubboMetadataConfigServiceProxy.newProxy(serviceName); - String serviceRestMetadataJsonConfig = dubboMetadataConfigService.getServiceRestMetadata(); - Set metadata; + Set 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 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); + } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/DubboTransportedAttributesResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/DubboTransportedAttributesResolver.java new file mode 100644 index 00000000..e403139e --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/DubboTransportedAttributesResolver.java @@ -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 Mercy + */ +public class DubboTransportedAttributesResolver { + + private final PropertyResolver propertyResolver; + + public DubboTransportedAttributesResolver(PropertyResolver propertyResolver) { + this.propertyResolver = propertyResolver; + } + + public Map resolve(DubboTransported dubboTransported) { + Map attributes = getAnnotationAttributes(dubboTransported); + return resolve(attributes); + } + + public Map resolve(Map attributes) { + Map resolvedAttributes = new LinkedHashMap<>(); + for (Map.Entry entry : attributes.entrySet()) { + Object value = entry.getValue(); + if (value instanceof String) { + value = propertyResolver.resolvePlaceholders(value.toString()); + } + resolvedAttributes.put(entry.getKey(), value); + } + return resolvedAttributes; + } +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/DubboTransportedMethodMetadataResolver.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/DubboTransportedMethodMetadataResolver.java index d1aa6389..3e232440 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/DubboTransportedMethodMetadataResolver.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/resolver/DubboTransportedMethodMetadataResolver.java @@ -42,12 +42,12 @@ public class DubboTransportedMethodMetadataResolver { private static final Class 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 attributes = attributesResolver.resolve(dubboTransported); + return new DubboTransportedMethodMetadata(method, attributes); } private DubboTransported resolveDubboTransported(Method method) { diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/DubboInvocationHandler.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/DubboInvocationHandler.java index ab4d18fa..01d038fb 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/DubboInvocationHandler.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/DubboInvocationHandler.java @@ -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 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); } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/TargeterBeanPostProcessor.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/TargeterBeanPostProcessor.java index 331e7a00..0419a157 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/TargeterBeanPostProcessor.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/TargeterBeanPostProcessor.java @@ -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)); } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/TargeterInvocationHandler.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/TargeterInvocationHandler.java index e606367a..6e9ca7f4 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/TargeterInvocationHandler.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/openfeign/TargeterInvocationHandler.java @@ -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 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 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); diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/AbstractRegistrationFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/AbstractRegistrationFactory.java deleted file mode 100644 index f17c3019..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/AbstractRegistrationFactory.java +++ /dev/null @@ -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 - *

- * - * @param The subclass of {@link Registration} - * @author Mercy - */ -public abstract class AbstractRegistrationFactory implements RegistrationFactory { - - 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); - } -} - diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/AbstractSpringCloudRegistry.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/AbstractSpringCloudRegistry.java new file mode 100644 index 00000000..c52cf231 --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/AbstractSpringCloudRegistry.java @@ -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 Mercy + */ +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 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 serviceNames) { + filter(serviceNames, new Filter() { + @Override + public boolean accept(String serviceName) { + return supports(serviceName); + } + }); + } + + protected abstract boolean supports(String serviceName); + + protected final Set 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 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 getServiceNamesForOps(URL url) { + Set serviceNames = getAllServiceNames(); + filterServiceNames(serviceNames); + return serviceNames; + } + + protected abstract String getServiceName(URL url); + + private void doSubscribe(final URL url, final NotifyListener listener, final Collection 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 getServiceInstances(String serviceName) { + return discoveryClient.getInstances(serviceName); + } + + private void subscribe(final URL url, final NotifyListener listener, final Collection serviceNames) { + for (String serviceName : serviceNames) { + List 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 serviceInstances); + + protected void filterHealthyInstances(Collection instances) { + filter(instances, new Filter() { + @Override + public boolean accept(ServiceInstance data) { + // TODO check the details of status +// return serviceRegistry.getStatus(new DubboRegistration(data)) != null; + return true; + } + }); + } + + protected List buildURLs(URL consumerURL, Collection serviceInstances) { + if (serviceInstances.isEmpty()) { + return Collections.emptyList(); + } + List urls = new LinkedList(); + 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 void filter(Collection collection, Filter filter) { + Iterator iterator = collection.iterator(); + while (iterator.hasNext()) { + T data = iterator.next(); + if (!filter.accept(data)) { // remove if not accept + iterator.remove(); + } + } + } + + private static T[] of(T... values) { + return values; + } + + /** + * A filter + */ + public interface Filter { + + /** + * Tests whether or not the specified data should be accepted. + * + * @param data The data to be tested + * @return true if and only if data + * should be accepted + */ + boolean accept(T data); + + } + +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/DubboServiceRegistrationEventPublishingAspect.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/DubboServiceRegistrationEventPublishingAspect.java new file mode 100644 index 00000000..17826624 --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/DubboServiceRegistrationEventPublishingAspect.java @@ -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 Mercy + * @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; + } +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/RegistrationFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/RegistrationFactory.java deleted file mode 100644 index d05eea94..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/RegistrationFactory.java +++ /dev/null @@ -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 The subclass of {@link Registration} - * @author Mercy - */ -public interface RegistrationFactory { - - /** - * 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); -} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/RegistrationFactoryProvider.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/RegistrationFactoryProvider.java deleted file mode 100644 index c5debc68..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/RegistrationFactoryProvider.java +++ /dev/null @@ -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 Mercy - */ -public class RegistrationFactoryProvider implements FactoryBean, 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 serviceRegistry = applicationContext.getBean(ServiceRegistry.class); - ClassLoader classLoader = applicationContext.getClassLoader(); - this.registrationFactory = buildRegistrationFactory(serviceRegistry, classLoader); - } - - private RegistrationFactory buildRegistrationFactory(ServiceRegistry serviceRegistry, - ClassLoader classLoader) { - RegistrationFactory registrationFactory = null; - List 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); - } - -} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistry.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistry.java index d87e1f6c..24db7366 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistry.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistry.java @@ -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 Mercy */ -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 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 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 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 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 serviceInstances) { + List 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 serviceNames) { - filter(serviceNames, new Filter() { - @Override - public boolean accept(String serviceName) { - return dubboRegistryServiceIdHandler.supports(serviceName); - } - }); - } - - private List 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 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 serviceNames = getAllServiceNames(); - filterServiceNames(serviceNames); - doSubscribe(url, listener, serviceNames); - } - }, allServicesLookupInterval, allServicesLookupInterval, TimeUnit.SECONDS); - } - - private void doSubscribe(final URL url, final NotifyListener listener, final List serviceNames) { - for (String serviceName : serviceNames) { - List 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 serviceInstances) { - List healthyInstances = new LinkedList(serviceInstances); - // Healthy Instances - filterHealthyInstances(healthyInstances); - List urls = buildURLs(url, healthyInstances); - this.notify(url, listener, urls); - } - - private void filterHealthyInstances(Collection instances) { - filter(instances, new Filter() { - @Override - public boolean accept(ServiceInstance data) { - // TODO check the details of status -// return serviceRegistry.getStatus(new DubboRegistration(data)) != null; - return true; - } - }); - } - - private List buildURLs(URL consumerURL, Collection serviceInstances) { - if (serviceInstances.isEmpty()) { - return Collections.emptyList(); - } - List urls = new LinkedList(); - 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 getServiceNamesForOps(URL url) { - List serviceNames = getAllServiceNames(); - filterServiceNames(serviceNames); - return serviceNames; - } - - private void filter(Collection collection, Filter filter) { - Iterator iterator = collection.iterator(); - while (iterator.hasNext()) { - T data = iterator.next(); - if (!filter.accept(data)) { // remove if not accept - iterator.remove(); - } - } - } - - private static T[] of(T... values) { - return values; - } - - /** - * A filter - */ - public interface Filter { - - /** - * Tests whether or not the specified data should be accepted. - * - * @param data The data to be tested - * @return true if and only if data - * should be accepted - */ - boolean accept(T data); - - } - } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistryFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistryFactory.java index 234c65d4..4a046801 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistryFactory.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/SpringCloudRegistryFactory.java @@ -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 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) { diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/apache/zookeeper/ZookeeperRegistrationFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/apache/zookeeper/ZookeeperRegistrationFactory.java deleted file mode 100644 index 6657343f..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/apache/zookeeper/ZookeeperRegistrationFactory.java +++ /dev/null @@ -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 Mercy - */ -public class ZookeeperRegistrationFactory extends AbstractRegistrationFactory { - - @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; - } -} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/event/ServiceInstancePreRegisteredEvent.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/event/ServiceInstancePreRegisteredEvent.java new file mode 100644 index 00000000..6233e9a5 --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/event/ServiceInstancePreRegisteredEvent.java @@ -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 Mercy + */ +public class ServiceInstancePreRegisteredEvent extends ApplicationEvent { + + public ServiceInstancePreRegisteredEvent(Registration source) { + super(source); + } + + @Override + public Registration getSource() { + return (Registration) super.getSource(); + } +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/DefaultRegistrationFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/event/ServiceInstanceRegisteredEvent.java similarity index 65% rename from spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/DefaultRegistrationFactory.java rename to spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/event/ServiceInstanceRegisteredEvent.java index 1bcfd534..8aab6aa7 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/DefaultRegistrationFactory.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/event/ServiceInstanceRegisteredEvent.java @@ -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 Mercy */ -public class DefaultRegistrationFactory extends AbstractRegistrationFactory { +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(); } } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/handler/DubboRegistryServiceIdHandler.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/handler/DubboRegistryServiceIdHandler.java deleted file mode 100644 index 15a35029..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/handler/DubboRegistryServiceIdHandler.java +++ /dev/null @@ -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 Mercy - */ -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 true, or false - */ - 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(); - -} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/handler/StandardDubboRegistryServiceIdHandler.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/handler/StandardDubboRegistryServiceIdHandler.java deleted file mode 100644 index caaa0706..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/handler/StandardDubboRegistryServiceIdHandler.java +++ /dev/null @@ -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} - *

- * The service ID pattern is "${category}:${interface}:${version}:${group}" - * - * @author Mercy - */ -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); - } - } -} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/hashicorp/consul/ConsulRegistrationFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/hashicorp/consul/ConsulRegistrationFactory.java deleted file mode 100644 index a5861fb9..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/hashicorp/consul/ConsulRegistrationFactory.java +++ /dev/null @@ -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 Mercy - */ -public class ConsulRegistrationFactory extends AbstractRegistrationFactory { - - @Override - public ConsulRegistration create(ServiceInstance serviceInstance, ConfigurableApplicationContext applicationContext) { - Map metadata = getMetadata(serviceInstance); - List 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 createTags(Map metadata) { - List tags = new LinkedList<>(); - for (Map.Entry entry : metadata.entrySet()) { - String tag = entry.getKey() + "=" + entry.getValue(); - tags.add(tag); - - } - return tags; - } - - private Map getMetadata(ServiceInstance serviceInstance) { - Map metadata = serviceInstance.getMetadata(); - Set removedKeys = new LinkedHashSet<>(); - for (String key : metadata.keySet()) { - if (key.contains(".")) { - removedKeys.add(key); - } - } - for (String removedKey : removedKeys) { - metadata.remove(removedKey); - } - return metadata; - } -} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/netflix/eureka/EurekaRegistrationFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/netflix/eureka/EurekaRegistrationFactory.java deleted file mode 100644 index 84fafa62..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/netflix/eureka/EurekaRegistrationFactory.java +++ /dev/null @@ -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 Mercy - */ -public class EurekaRegistrationFactory extends AbstractRegistrationFactory { - - @Override - public EurekaRegistration create(ServiceInstance serviceInstance, ConfigurableApplicationContext applicationContext) { - CloudEurekaInstanceConfig cloudEurekaInstanceConfig = applicationContext.getBean(CloudEurekaInstanceConfig.class); - ObjectProvider 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(); - } -} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/DubboGenericServiceFactory.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/DubboGenericServiceFactory.java index e15bbb69..d1648641 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/DubboGenericServiceFactory.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/DubboGenericServiceFactory.java @@ -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> cache = new ConcurrentHashMap<>(); - public GenericService create(DubboServiceMetadata dubboServiceMetadata, - DubboTransportedMetadata dubboTransportedMetadata) { + public GenericService create(DubboRestServiceMetadata dubboServiceMetadata, + Map dubboTranslatedAttributes) { - ReferenceBean referenceBean = build(dubboServiceMetadata.getServiceRestMetadata(), dubboTransportedMetadata); + ReferenceBean referenceBean = build(dubboServiceMetadata.getServiceRestMetadata(), dubboTranslatedAttributes); return referenceBean == null ? null : referenceBean.get(); } public GenericService create(String serviceName, Class serviceClass) { String interfaceName = serviceClass.getName(); - ReferenceBean referenceBean = build(interfaceName, serviceName, null, - DEFAULT_PROTOCOL, DEFAULT_CLUSTER); + ReferenceBean referenceBean = build(interfaceName, serviceName, null, emptyMap()); return referenceBean.get(); } private ReferenceBean build(ServiceRestMetadata serviceRestMetadata, - DubboTransportedMetadata dubboTransportedMetadata) { + Map 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 build(String interfaceName, String version, String group, String protocol, - String cluster) { + private ReferenceBean build(String interfaceName, String version, String group, + Map dubboTranslatedAttributes) { - Integer key = Objects.hash(interfaceName, version, group, protocol, cluster); + Integer key = Objects.hash(interfaceName, version, group, dubboTranslatedAttributes); ReferenceBean 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 referenceBean, Map 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 parameters = CollectionUtils.toStringMap(commaDelimitedListToStringArray(content)); + setValue(parameters); + } + }); + + dataBinder.bind(new MutablePropertyValues(dubboTranslatedAttributes)); + } + @PreDestroy public void destroy() { destroyReferenceBeans(); diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/DubboMetadataConfigServiceExporter.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/DubboMetadataConfigServiceExporter.java new file mode 100644 index 00000000..a47d1895 --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/DubboMetadataConfigServiceExporter.java @@ -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 Mercy + */ +@Component +public class DubboMetadataConfigServiceExporter { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private ApplicationConfig applicationConfig; + + @Autowired + private PublishingDubboMetadataConfigService dubboMetadataConfigService; + + @Autowired + private Supplier protocolConfigSupplier; + + @Value("${spring.application.name:application}") + private String currentApplicationName; + + /** + * The ServiceConfig of DubboMetadataConfigService to be exported, can be nullable. + */ + private ServiceConfig 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()); + } + } +} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/PublishingDubboMetadataConfigService.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/PublishingDubboMetadataConfigService.java index fa49e94b..76fe660f 100644 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/PublishingDubboMetadataConfigService.java +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/service/PublishingDubboMetadataConfigService.java @@ -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 = 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; } diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/util/JSONUtils.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/util/JSONUtils.java new file mode 100644 index 00000000..1dd1f6da --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/util/JSONUtils.java @@ -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 Mercy + */ +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 toList(String json) { + List 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; + } +} diff --git a/spring-cloud-alibaba-dubbo/src/main/resources/META-INF/spring.factories b/spring-cloud-alibaba-dubbo/src/main/resources/META-INF/spring.factories index ed1c9c3f..46ad6959 100644 --- a/spring-cloud-alibaba-dubbo/src/main/resources/META-INF/spring.factories +++ b/spring-cloud-alibaba-dubbo/src/main/resources/META-INF/spring.factories @@ -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 \ No newline at end of file + +org.springframework.boot.env.EnvironmentPostProcessor=\ +org.springframework.cloud.alibaba.dubbo.env.DubboNonWebApplicationEnvironmentPostProcessor \ No newline at end of file diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/NacosDiscoveryAutoConfiguration.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/NacosDiscoveryAutoConfiguration.java index 97e04060..c092f62a 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/NacosDiscoveryAutoConfiguration.java +++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/NacosDiscoveryAutoConfiguration.java @@ -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() - } - }; - } } \ No newline at end of file diff --git a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/discovery/NacosDiscoveryClientAutoConfiguration.java b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/discovery/NacosDiscoveryClientAutoConfiguration.java index 2ddb4c34..2c1433ae 100644 --- a/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/discovery/NacosDiscoveryClientAutoConfiguration.java +++ b/spring-cloud-alibaba-nacos-discovery/src/main/java/org/springframework/cloud/alibaba/nacos/discovery/NacosDiscoveryClientAutoConfiguration.java @@ -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) {