diff --git a/spring-cloud-alibaba-dubbo/pom.xml b/spring-cloud-alibaba-dubbo/pom.xml index b713a8bd..25750683 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.1.0.RELEASE 2.1.0.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/autoconfigure/DubboMetadataAutoConfiguration.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboMetadataAutoConfiguration.java index a853a3fb..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 @@ -17,20 +17,28 @@ package org.springframework.cloud.alibaba.dubbo.autoconfigure; 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.function.Supplier; @@ -41,9 +49,21 @@ import java.util.function.Supplier; * @author Mercy */ @Configuration -@Import({DubboServiceMetadataRepository.class, PublishingDubboMetadataConfigService.class}) +@Import({DubboServiceMetadataRepository.class, + PublishingDubboMetadataConfigService.class, + DubboMetadataConfigServiceExporter.class, + JSONUtils.class}) public class DubboMetadataAutoConfiguration { + @Autowired + private PublishingDubboMetadataConfigService dubboMetadataConfigService; + + @Autowired + private MetadataResolver metadataResolver; + + @Autowired + private DubboMetadataConfigServiceExporter dubboMetadataConfigServiceExporter; + @Bean @ConditionalOnMissingBean public MetadataResolver metadataJsonResolver(ObjectProvider contract) { @@ -51,7 +71,7 @@ public class DubboMetadataAutoConfiguration { } @Bean - public Supplier dubboProtocolConfigSupplier(Collection protocols) { + public Supplier dubboProtocolConfigSupplier(ObjectProvider> protocols) { return new DubboProtocolConfigSupplier(protocols); } @@ -60,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 e91e03c3..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/autoconfigure/DubboMetadataEventHandlingAutoConfiguration.java +++ /dev/null @@ -1,212 +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.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 java.util.function.Supplier; - -/** - * 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 - private Supplier protocolConfigSupplier; - - @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(protocolConfigSupplier.get()); - - 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 38eac2cc..8c36342a 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,15 @@ 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.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.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; @@ -71,17 +68,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 * 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..081e5b6e --- /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.registry.SpringCloudRegistry.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 9920e8c5..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 @@ -20,7 +20,7 @@ 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.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; @@ -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, dubboTranslatedAttributes); + 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/metadata/DubboProtocolConfigSupplier.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/DubboProtocolConfigSupplier.java index 5eb3adfb..d5d99430 100644 --- 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 @@ -18,6 +18,8 @@ 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; @@ -31,15 +33,16 @@ import static org.apache.dubbo.common.Constants.DEFAULT_PROTOCOL; */ public class DubboProtocolConfigSupplier implements Supplier { - private final Collection protocols; + private final ObjectProvider> protocols; - public DubboProtocolConfigSupplier(Collection 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)) { 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/repository/DubboServiceMetadataRepository.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/metadata/repository/DubboServiceMetadataRepository.java index e8d21a31..e4a58102 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,13 +16,15 @@ */ 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.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; @@ -30,6 +32,7 @@ import org.springframework.cloud.alibaba.dubbo.service.DubboMetadataConfigServic import org.springframework.http.HttpRequest; import org.springframework.stereotype.Repository; +import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; import java.util.LinkedHashSet; @@ -51,17 +54,31 @@ public class DubboServiceMetadataRepository { private final ObjectMapper objectMapper = new ObjectMapper(); + private final Set registeredURLs = new LinkedHashSet<>(); + /** * Key is application name - * Value is Map + * Value is Map */ - private Map> repository = newHashMap(); + private Map> repository = newHashMap(); @Autowired private DubboMetadataConfigServiceProxy dubboMetadataConfigServiceProxy; + 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); + } + /** - * Initialize the specified service's Dubbo Service Metadata + * Initialize the specified service's {@link ServiceRestMetadata} * * @param serviceName the service name */ @@ -81,14 +98,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,13 +116,13 @@ 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) { + public DubboRestServiceMetadata get(String serviceName, RequestMetadata requestMetadata) { return match(repository, serviceName, requestMetadata); } @@ -148,7 +165,7 @@ public class DubboServiceMetadataRepository { return object; } - private Map getMetadataMap(String serviceName) { + private Map getMetadataMap(String serviceName) { return getMap(repository, serviceName); } 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 9ef731dd..fa93bb27 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 @@ -24,7 +24,7 @@ import feign.Target; 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.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; @@ -147,13 +147,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(); Map dubboTranslatedAttributes = dubboTransportedMethodMetadata.getAttributes(); Method method = dubboTransportedMethodMetadata.getMethod(); - GenericService dubboGenericService = dubboGenericServiceFactory.create(dubboServiceMetadata, dubboTranslatedAttributes); - 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..5fb3d2e4 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,138 @@ */ 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.alibaba.dubbo.util.JSONUtils; 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 org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedList; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; -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 static java.util.Collections.emptyMap; +import static org.apache.dubbo.common.Constants.APPLICATION_KEY; +import static org.springframework.util.CollectionUtils.isEmpty; /** * 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} + * The property name of Dubbo {@link URL URLs} metadata */ - public static final String ALL_SERVICES_LOOKUP_INTERVAL_PARAM_NAME = "dubbo.all.services.lookup.interval"; + public static final String DUBBO_URLS_METADATA_PROPERTY_NAME = "dubbo.urls"; /** - * The parameter name of {@link #registeredServicesLookupInterval} + * The parameter name of the services of Dubbo Provider */ - public static final String REGISTERED_SERVICES_LOOKUP_INTERVAL_PARAM_NAME = "dubbo.registered.services.lookup.interval"; + public static final String DUBBO_PROVIDER_SERVICES_PARAM_NAME = "dubbo.provider-services"; /** - * All supported categories + * All services of Dubbo Provider */ - public static final String[] ALL_SUPPORTED_CATEGORIES = of( - PROVIDERS_CATEGORY, - CONSUMERS_CATEGORY, - ROUTERS_CATEGORY, - CONFIGURATORS_CATEGORY - ); + public static final String ALL_DUBBO_PROVIDER_SERVICES = "*"; - private final Logger logger = LoggerFactory.getLogger(getClass()); + private final DubboServiceMetadataRepository dubboServiceMetadataRepository; - /** - * The interval in second of lookup service names(only for Dubbo-OPS) - */ - private final long allServicesLookupInterval; + private final JSONUtils jsonUtils; - private final long registeredServicesLookupInterval; + private final Set dubboProviderServices; - private final ServiceRegistry serviceRegistry; + private final Map dubboServiceKeysCache; - 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, + DubboServiceMetadataRepository dubboServiceMetadataRepository, 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; + super(url, discoveryClient, servicesLookupScheduler); + this.dubboServiceMetadataRepository = dubboServiceMetadataRepository; + this.jsonUtils = applicationContext.getBean(JSONUtils.class); + this.dubboProviderServices = getDubboProviderServices(); + this.dubboServiceKeysCache = this.initDubboServiceKeysCache(); } - protected boolean shouldRegister(Registration registration) { - Map metadata = registration.getMetadata(); - String side = metadata.get(SIDE_KEY); - return PROVIDER_SIDE.equals(side); // Only register the Provider. + private Map initDubboServiceKeysCache() { + + if (isEmpty(dubboProviderServices)) { + return emptyMap(); + } + + Map newCache = new HashMap<>(); + + dubboProviderServices.stream() + .map(this::getServiceInstances) + .filter(this::isNotEmpty) + .forEach(serviceInstances -> { + ServiceInstance serviceInstance = serviceInstances.get(0); + getURLs(serviceInstance).forEach(url -> { + String serviceKey = url.getServiceKey(); + String serviceName = url.getParameter(APPLICATION_KEY); + newCache.put(serviceKey, serviceName); + }); + }); + + return newCache; + } + + private boolean isNotEmpty(Collection collection) { + return !CollectionUtils.isEmpty(collection); + } + + private List getURLs(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()); + } + + private Set getDubboProviderServices() { + URL registryURL = getUrl(); + String services = registryURL.getParameter(DUBBO_PROVIDER_SERVICES_PARAM_NAME, ALL_DUBBO_PROVIDER_SERVICES); + return ALL_DUBBO_PROVIDER_SERVICES.equalsIgnoreCase(services) ? + getAllServiceNames() : StringUtils.commaDelimitedListToSet(services); } @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 dubboProviderServices.contains(serviceName); } @Override - public void doUnsubscribe(URL url, NotifyListener listener) { - if (isAdminProtocol(url)) { - shutdownServiceNamesLookup(); - } + protected String getServiceName(URL url) { + String serviceKey = url.getServiceKey(); + return dubboServiceKeysCache.get(serviceKey); } @Override - public boolean isAvailable() { - return !discoveryClient.getServices().isEmpty(); + protected void notifySubscriber(URL url, NotifyListener listener, List serviceInstances) { + List urls = serviceInstances.stream().map(this::getURLs) + .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..15dd3e5d 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,15 @@ 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, applicationContext); } 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 23638325..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/apache/zookeeper/ZookeeperRegistrationFactory.java +++ /dev/null @@ -1,52 +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(); - - // To trigger build() method - registration.getServiceInstance(); - - return registration; - } -} diff --git a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/env/DubboNonWebApplicationEnvironmentPostProcessor.java b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/env/DubboNonWebApplicationEnvironmentPostProcessor.java new file mode 100644 index 00000000..60faa7c3 --- /dev/null +++ b/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/env/DubboNonWebApplicationEnvironmentPostProcessor.java @@ -0,0 +1,199 @@ +/* + * 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.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.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; + } + + resetServerPort(environment); + } + + /** + * Reset server port property if it's absent, whose value is configured by "dubbbo.protocol.port" + * or "dubbo.protcols.rest.port" + * + * @param environment + */ + private void resetServerPort(ConfigurableEnvironment environment) { + + 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); + + } + + 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) { + if (serverPort == null) { + return; + } + + MutablePropertySources propertySources = environment.getPropertySources(); + + Map properties = new HashMap<>(); + properties.put(SERVER_PORT_PROPERTY_NAME, String.valueOf(serverPort)); + + addOrReplace(propertySources, properties); + } + + /** + * 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/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 e72bf40f..00000000 --- a/spring-cloud-alibaba-dubbo/src/main/java/org/springframework/cloud/alibaba/dubbo/registry/netflix/eureka/EurekaRegistrationFactory.java +++ /dev/null @@ -1,60 +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.setNonSecurePort(serviceInstance.getPort()); - 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 10cd08c9..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 @@ -25,7 +25,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.propertyeditors.StringTrimmerEditor; -import org.springframework.cloud.alibaba.dubbo.metadata.DubboServiceMetadata; +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; @@ -54,7 +54,7 @@ public class DubboGenericServiceFactory { private final ConcurrentMap> cache = new ConcurrentHashMap<>(); - public GenericService create(DubboServiceMetadata dubboServiceMetadata, + public GenericService create(DubboRestServiceMetadata dubboServiceMetadata, Map dubboTranslatedAttributes) { ReferenceBean referenceBean = build(dubboServiceMetadata.getServiceRestMetadata(), dubboTranslatedAttributes); 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..515af6f5 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.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.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.registry.env.DubboNonWebApplicationEnvironmentPostProcessor \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/pom.xml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/pom.xml index 6d548b81..424a48c8 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/pom.xml +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/pom.xml @@ -20,6 +20,7 @@ spring-cloud-dubbo-provider-sample spring-cloud-dubbo-consumer-sample spring-cloud-dubbo-provider-web-sample + spring-cloud-dubbo-servlet-gateway-sample diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudConsumerBootstrap.java b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudConsumerBootstrap.java index 8c2a0c9e..2e389ce2 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudConsumerBootstrap.java +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudConsumerBootstrap.java @@ -17,6 +17,7 @@ package org.springframework.cloud.alibaba.dubbo.bootstrap; import org.apache.dubbo.config.annotation.Reference; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.ApplicationRunner; @@ -52,7 +53,7 @@ import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE; @EnableFeignClients public class DubboSpringCloudConsumerBootstrap { - @Reference(version = "1.0.0") + @Reference(version = "1.0.0", protocol = "dubbo") private RestService restService; @Autowired @@ -94,7 +95,7 @@ public class DubboSpringCloudConsumerBootstrap { } @FeignClient("${provider.application.name}") - @DubboTransported() + @DubboTransported(protocol = "dubbo") public interface DubboFeignRestService { @GetMapping(value = "/param") diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/src/main/resources/application.yaml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/src/main/resources/application.yaml index 263a5699..41c67146 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/src/main/resources/application.yaml +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-consumer-sample/src/main/resources/application.yaml @@ -9,4 +9,4 @@ server: provider: application: - name: spring-cloud-alibaba-dubbo-web-provider \ No newline at end of file + name: spring-cloud-alibaba-dubbo-provider \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-sample/pom.xml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-sample/pom.xml index 3f5c35cb..14424d8a 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-sample/pom.xml +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-sample/pom.xml @@ -17,6 +17,13 @@ + + + javax.servlet + javax.servlet-api + 3.1.0 + + org.springframework diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudProviderBootstrap.java b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudProviderBootstrap.java index 4606a190..8e6ed4e9 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudProviderBootstrap.java +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudProviderBootstrap.java @@ -16,6 +16,7 @@ */ package org.springframework.cloud.alibaba.dubbo.bootstrap; +import org.springframework.boot.WebApplicationType; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @@ -30,6 +31,7 @@ public class DubboSpringCloudProviderBootstrap { public static void main(String[] args) { new SpringApplicationBuilder(DubboSpringCloudProviderBootstrap.class) .properties("spring.profiles.active=nacos") + .web(WebApplicationType.NONE) .run(args); } } diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-web-sample/src/main/resources/bootstrap.yaml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-web-sample/src/main/resources/bootstrap.yaml index fd82247b..0d8dfca9 100644 --- a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-web-sample/src/main/resources/bootstrap.yaml +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-provider-web-sample/src/main/resources/bootstrap.yaml @@ -1,6 +1,6 @@ spring: application: - name: spring-cloud-alibaba-dubbo-web-provider + name: spring-cloud-alibaba-dubbo-provider main: allow-bean-definition-overriding: true diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/pom.xml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/pom.xml new file mode 100644 index 00000000..75b6d18e --- /dev/null +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/pom.xml @@ -0,0 +1,39 @@ + + + + spring-cloud-alibaba-dubbo-examples + org.springframework.cloud + 0.2.2.BUILD-SNAPSHOT + ../pom.xml + + 4.0.0 + + org.springframework.cloud + spring-cloud-dubbo-servlet-gateway-sample + Spring Cloud Dubbo Servlet Gateway Sample + + + + + org.springframework.boot + spring-boot-starter-web + + + + + org.springframework.cloud + spring-cloud-dubbo-sample-api + ${project.version} + + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + + \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudServletGatewayBootstrap.java b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudServletGatewayBootstrap.java new file mode 100644 index 00000000..0ad4d68e --- /dev/null +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/bootstrap/DubboSpringCloudServletGatewayBootstrap.java @@ -0,0 +1,23 @@ +package org.springframework.cloud.alibaba.dubbo.bootstrap; + +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.ServletComponentScan; +import org.springframework.cloud.client.discovery.EnableDiscoveryClient; +import org.springframework.cloud.openfeign.EnableFeignClients; + +/** + * Dubbo Spring Cloud Servlet Gateway Bootstrap + */ +@EnableDiscoveryClient +@EnableAutoConfiguration +@EnableFeignClients +@ServletComponentScan(basePackages = "org.springframework.cloud.alibaba.dubbo.gateway") +public class DubboSpringCloudServletGatewayBootstrap { + + public static void main(String[] args) { + new SpringApplicationBuilder(DubboSpringCloudServletGatewayBootstrap.class) + .properties("spring.profiles.active=nacos") + .run(args); + } +} diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/gateway/DubboGatewayServlet.java b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/gateway/DubboGatewayServlet.java new file mode 100644 index 00000000..0bff5e0c --- /dev/null +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/src/main/java/org/springframework/cloud/alibaba/dubbo/gateway/DubboGatewayServlet.java @@ -0,0 +1,188 @@ +//package org.springframework.cloud.alibaba.dubbo.gateway; +// +//import org.apache.commons.lang.StringUtils; +//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.RequestMetadata; +//import org.springframework.cloud.alibaba.dubbo.metadata.RestMethodMetadata; +//import org.springframework.cloud.alibaba.dubbo.metadata.repository.DubboServiceMetadataRepository; +//import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceExecutionContext; +//import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceExecutionContextFactory; +//import org.springframework.cloud.alibaba.dubbo.service.DubboGenericServiceFactory; +//import org.springframework.http.HttpHeaders; +//import org.springframework.http.HttpRequest; +//import org.springframework.util.AntPathMatcher; +//import org.springframework.util.CollectionUtils; +//import org.springframework.util.PathMatcher; +//import org.springframework.util.StreamUtils; +//import org.springframework.web.util.UriComponents; +// +//import javax.servlet.ServletException; +//import javax.servlet.ServletInputStream; +//import javax.servlet.annotation.WebServlet; +//import javax.servlet.http.HttpServlet; +//import javax.servlet.http.HttpServletRequest; +//import javax.servlet.http.HttpServletResponse; +//import java.io.IOException; +//import java.net.URI; +//import java.net.URISyntaxException; +//import java.util.*; +// +//import static org.springframework.web.util.UriComponentsBuilder.fromUriString; +// +//@WebServlet(urlPatterns = "/dsc/*") +//public class DubboGatewayServlet extends HttpServlet { +// +// private final DubboServiceMetadataRepository repository; +// +// private final DubboTransportedMetadata dubboTransportedMetadata; +// +// private final DubboGenericServiceFactory serviceFactory; +// +// private final DubboGenericServiceExecutionContextFactory contextFactory; +// +// private final PathMatcher pathMatcher = new AntPathMatcher(); +// +// public DubboGatewayServlet(DubboServiceMetadataRepository repository, +// DubboGenericServiceFactory serviceFactory, +// DubboGenericServiceExecutionContextFactory contextFactory) { +// this.repository = repository; +// this.dubboTransportedMetadata = new DubboTransportedMetadata(); +// dubboTransportedMetadata.setProtocol("dubbo"); +// dubboTransportedMetadata.setCluster("failover"); +// this.serviceFactory = serviceFactory; +// this.contextFactory = contextFactory; +// } +// +// public void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { +// +// // /g/{app-name}/{rest-path} +// String requestURI = request.getRequestURI(); +// // /g/ +// String servletPath = request.getServletPath(); +// +// String part = StringUtils.substringAfter(requestURI, servletPath); +// +// String serviceName = StringUtils.substringBetween(part, "/", "/"); +// +// // App name= spring-cloud-alibaba-dubbo-web-provider (127.0.0.1:8080) +// +// String restPath = StringUtils.substringAfter(part, serviceName); +// +// // 初始化 serviceName 的 REST 请求元数据 +// repository.initialize(serviceName); +// // 将 HttpServletRequest 转化为 RequestMetadata +// RequestMetadata clientMetadata = buildRequestMetadata(request, restPath); +// +// DubboServiceMetadata dubboServiceMetadata = repository.get(serviceName, clientMetadata); +// +// if (dubboServiceMetadata == null) { +// // if DubboServiceMetadata is not found, executes next +// throw new ServletException("DubboServiceMetadata can't be found!"); +// } +// +// RestMethodMetadata dubboRestMethodMetadata = dubboServiceMetadata.getRestMethodMetadata(); +// +// GenericService genericService = serviceFactory.create(dubboServiceMetadata, dubboTransportedMetadata); +// +// // TODO: Get the Request Body from HttpServletRequest +// byte[] body = getRequestBody(request); +// +// MutableHttpServerRequest httpServerRequest = new MutableHttpServerRequest(new HttpRequestAdapter(request), body); +// +//// customizeRequest(httpServerRequest, dubboRestMethodMetadata, clientMetadata); +// +// DubboGenericServiceExecutionContext context = contextFactory.create(dubboRestMethodMetadata, httpServerRequest); +// +// Object result = null; +// GenericException exception = null; +// +// try { +// result = genericService.$invoke(context.getMethodName(), context.getParameterTypes(), context.getParameters()); +// } catch (GenericException e) { +// exception = e; +// } +// response.getWriter().println(result); +// } +// +// private byte[] getRequestBody(HttpServletRequest request) throws IOException { +// ServletInputStream inputStream = request.getInputStream(); +// return StreamUtils.copyToByteArray(inputStream); +// } +// +// private static class HttpRequestAdapter implements HttpRequest { +// +// private final HttpServletRequest request; +// +// private HttpRequestAdapter(HttpServletRequest request) { +// this.request = request; +// } +// +// @Override +// public String getMethodValue() { +// return request.getMethod(); +// } +// +// @Override +// public URI getURI() { +// try { +// return new URI(request.getRequestURL().toString() + "?" + request.getQueryString()); +// } catch (URISyntaxException e) { +// e.printStackTrace(); +// } +// throw new RuntimeException(); +// } +// +// @Override +// public HttpHeaders getHeaders() { +// return new HttpHeaders(); +// } +// } +// +//// protected void customizeRequest(MutableHttpServerRequest httpServerRequest, +//// RestMethodMetadata dubboRestMethodMetadata, RequestMetadata clientMetadata) { +//// +//// RequestMetadata dubboRequestMetadata = dubboRestMethodMetadata.getRequest(); +//// String pathPattern = dubboRequestMetadata.getPath(); +//// +//// Map pathVariables = pathMatcher.extractUriTemplateVariables(pathPattern, httpServerRequest.getPath()); +//// +//// if (!CollectionUtils.isEmpty(pathVariables)) { +//// // Put path variables Map into query parameters Map +//// httpServerRequest.params(pathVariables); +//// } +//// +//// } +// +// private RequestMetadata buildRequestMetadata(HttpServletRequest request, String restPath) { +// UriComponents uriComponents = fromUriString(request.getRequestURI()).build(true); +// RequestMetadata requestMetadata = new RequestMetadata(); +// requestMetadata.setPath(restPath); +// requestMetadata.setMethod(request.getMethod()); +// requestMetadata.setParams(getParams(request)); +// requestMetadata.setHeaders(getHeaders(request)); +// return requestMetadata; +// } +// +// private Map> getHeaders(HttpServletRequest request) { +// Map> map = new LinkedHashMap<>(); +// Enumeration headerNames = request.getHeaderNames(); +// while (headerNames.hasMoreElements()) { +// String headerName = headerNames.nextElement(); +// Enumeration headerValues = request.getHeaders(headerName); +// map.put(headerName, Collections.list(headerValues)); +// } +// return map; +// } +// +// private Map> getParams(HttpServletRequest request) { +// Map> map = new LinkedHashMap<>(); +// for (Map.Entry entry : request.getParameterMap().entrySet()) { +// map.put(entry.getKey(), Arrays.asList(entry.getValue())); +// } +// return map; +// } +//} diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/src/main/resources/application.yaml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/src/main/resources/application.yaml new file mode 100644 index 00000000..263a5699 --- /dev/null +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/src/main/resources/application.yaml @@ -0,0 +1,12 @@ +dubbo: + registry: + # The Spring Cloud Dubbo's registry extension + address: spring-cloud://localhost +# The traditional Dubbo's registry +# address: zookeeper://127.0.0.1:2181 +server: + port: 0 + +provider: + application: + name: spring-cloud-alibaba-dubbo-web-provider \ No newline at end of file diff --git a/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/src/main/resources/bootstrap.yaml b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/src/main/resources/bootstrap.yaml new file mode 100644 index 00000000..a86acd15 --- /dev/null +++ b/spring-cloud-alibaba-examples/spring-cloud-alibaba-dubbo-examples/spring-cloud-dubbo-servlet-gateway-sample/src/main/resources/bootstrap.yaml @@ -0,0 +1,70 @@ +spring: + application: + name: spring-cloud-alibaba-dubbo-servlet-gateway + main: + allow-bean-definition-overriding: true + + + # default disable all + cloud: + nacos: + discovery: + enabled: false + register-enabled: false + zookeeper: + enabled: false + consul: + enabled: false + +eureka: + client: + enabled: false + +ribbon: + nacos: + enabled: false + +--- +spring: + profiles: nacos + + cloud: + nacos: + discovery: + enabled: true + register-enabled: true + server-addr: 127.0.0.1:8848 + +ribbon: + nacos: + enabled: true + +--- +spring: + profiles: eureka + +eureka: + client: + enabled: true + service-url: + defaultZone: http://127.0.0.1:8761/eureka/ + + +--- +spring: + profiles: zookeeper + cloud: + zookeeper: + enabled: true + connect-string: 127.0.0.1:2181 + + +--- +spring: + profiles: consul + + cloud: + consul: + enabled: true + host: 127.0.0.1 + port: 8500 \ 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..6e6c2008 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; @@ -77,26 +75,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