Enhancement and Bugfix in 2.7.1 ( Part 3 ) (#3675)
* Polish /apache/incubator-dubbo#1306 : @Reference bean name conflict * Polish /apache/incubator-dubbo#3582 : service register support on nacos * Polish /apache/incubator-dubbo#3582 : Add license header and remove hard-code version * Polish /apache/incubator-dubbo#3582 : Remove junit * Polish /apache/incubator-dubbo#3582 : Translate to English
This commit is contained in:
parent
fcdf9c8ce7
commit
7fefb8145c
|
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* 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.apache.dubbo.config.spring.beans.factory.annotation;
|
||||
|
||||
import org.apache.dubbo.common.Constants;
|
||||
import org.apache.dubbo.config.annotation.Reference;
|
||||
import org.apache.dubbo.config.annotation.Service;
|
||||
import org.apache.dubbo.registry.Registry;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
import static org.apache.dubbo.common.Constants.CONSUMERS_CATEGORY;
|
||||
import static org.apache.dubbo.common.Constants.DEFAULT_PROTOCOL;
|
||||
import static org.apache.dubbo.common.Constants.PROVIDERS_CATEGORY;
|
||||
import static org.apache.dubbo.config.spring.util.AnnotationUtils.resolveInterfaceName;
|
||||
import static org.springframework.util.StringUtils.arrayToCommaDelimitedString;
|
||||
import static org.springframework.util.StringUtils.hasText;
|
||||
|
||||
/**
|
||||
* The Bean Name Builder for the annotations {@link Service} and {@link Reference}
|
||||
* <p>
|
||||
* The naming rule is consistent with the the implementation {@link Registry} that is based on the service-name aware
|
||||
* infrastructure, e.g Spring Cloud, Cloud Native and so on.
|
||||
* <p>
|
||||
* The pattern of bean name : ${category}:${protocol}:${serviceInterface}:${version}:${group}.
|
||||
* <p>
|
||||
* ${version} and ${group} are optional.
|
||||
*
|
||||
* @since 2.6.6
|
||||
*/
|
||||
class AnnotationBeanNameBuilder {
|
||||
|
||||
private static final String SEPARATOR = ":";
|
||||
|
||||
// Required properties
|
||||
|
||||
private final String category;
|
||||
|
||||
private final String protocol;
|
||||
|
||||
private final String interfaceClassName;
|
||||
|
||||
// Optional properties
|
||||
|
||||
private String version;
|
||||
|
||||
private String group;
|
||||
|
||||
private Environment environment;
|
||||
|
||||
private AnnotationBeanNameBuilder(String category, String protocol, String interfaceClassName) {
|
||||
this.category = category;
|
||||
this.protocol = protocol;
|
||||
this.interfaceClassName = interfaceClassName;
|
||||
}
|
||||
|
||||
private AnnotationBeanNameBuilder(Service service, Class<?> interfaceClass) {
|
||||
this(PROVIDERS_CATEGORY, resolveProtocol(service.protocol()), resolveInterfaceName(service, interfaceClass));
|
||||
this.group(service.group());
|
||||
this.version(service.version());
|
||||
}
|
||||
|
||||
private AnnotationBeanNameBuilder(Reference reference, Class<?> interfaceClass) {
|
||||
this(CONSUMERS_CATEGORY, resolveProtocol(reference.protocol()), resolveInterfaceName(reference, interfaceClass));
|
||||
this.group(reference.group());
|
||||
this.version(reference.version());
|
||||
}
|
||||
|
||||
public static AnnotationBeanNameBuilder create(Service service, Class<?> interfaceClass) {
|
||||
return new AnnotationBeanNameBuilder(service, interfaceClass);
|
||||
}
|
||||
|
||||
public static AnnotationBeanNameBuilder create(Reference reference, Class<?> interfaceClass) {
|
||||
return new AnnotationBeanNameBuilder(reference, interfaceClass);
|
||||
}
|
||||
|
||||
private static void append(StringBuilder builder, String value) {
|
||||
if (hasText(value)) {
|
||||
builder.append(SEPARATOR).append(value);
|
||||
}
|
||||
}
|
||||
|
||||
public AnnotationBeanNameBuilder group(String group) {
|
||||
this.group = group;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationBeanNameBuilder version(String version) {
|
||||
this.version = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AnnotationBeanNameBuilder environment(Environment environment) {
|
||||
this.environment = environment;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the protocol
|
||||
*
|
||||
* @param protocols one or more protocols
|
||||
* @return if <code>protocols</code> == <code>null</code>, it will return
|
||||
* {@link Constants#DEFAULT_PROTOCOL "dubbo"} as the default protocol
|
||||
* @see Constants#DEFAULT_PROTOCOL
|
||||
*/
|
||||
private static String resolveProtocol(String... protocols) {
|
||||
String protocol = arrayToCommaDelimitedString(protocols);
|
||||
return hasText(protocol) ? protocol : DEFAULT_PROTOCOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build bean name while resolve the placeholders if possible.
|
||||
*
|
||||
* @return pattern : ${category}:${protocol}:${serviceInterface}:${version}:${group}
|
||||
*/
|
||||
public String build() {
|
||||
// Append the required properties
|
||||
StringBuilder beanNameBuilder = new StringBuilder(category);
|
||||
append(beanNameBuilder, protocol);
|
||||
append(beanNameBuilder, interfaceClassName);
|
||||
// Append the optional properties
|
||||
append(beanNameBuilder, version);
|
||||
append(beanNameBuilder, group);
|
||||
String beanName = beanNameBuilder.toString();
|
||||
// Resolve placeholders
|
||||
return environment != null ? environment.resolvePlaceholders(beanName) : beanName;
|
||||
}
|
||||
}
|
||||
|
|
@ -188,7 +188,9 @@ public class ReferenceAnnotationBeanPostProcessor extends AnnotationInjectedBean
|
|||
|
||||
private String buildReferencedBeanName(Reference reference, Class<?> injectedType) {
|
||||
|
||||
ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(reference, injectedType, getEnvironment());
|
||||
AnnotationBeanNameBuilder builder = AnnotationBeanNameBuilder.create(reference, injectedType);
|
||||
|
||||
builder.environment(getEnvironment());
|
||||
|
||||
return getEnvironment().resolvePlaceholders(builder.build());
|
||||
}
|
||||
|
|
@ -261,4 +263,4 @@ public class ReferenceAnnotationBeanPostProcessor extends AnnotationInjectedBean
|
|||
this.injectedFieldReferenceBeanCache.clear();
|
||||
this.injectedMethodReferenceBeanCache.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -18,7 +18,6 @@ package org.apache.dubbo.config.spring.beans.factory.annotation;
|
|||
|
||||
import org.apache.dubbo.common.logger.Logger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.config.MethodConfig;
|
||||
import org.apache.dubbo.config.annotation.Service;
|
||||
import org.apache.dubbo.config.spring.ServiceBean;
|
||||
import org.apache.dubbo.config.spring.context.annotation.DubboClassPathBeanDefinitionScanner;
|
||||
|
|
@ -290,8 +289,9 @@ public class ServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistr
|
|||
*/
|
||||
private String generateServiceBeanName(Service service, Class<?> interfaceClass, String annotatedServiceBeanName) {
|
||||
|
||||
ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(service, interfaceClass, environment);
|
||||
AnnotationBeanNameBuilder builder = AnnotationBeanNameBuilder.create(service, interfaceClass);
|
||||
|
||||
builder.environment(environment);
|
||||
|
||||
return builder.build();
|
||||
|
||||
|
|
@ -316,8 +316,9 @@ public class ServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistr
|
|||
}
|
||||
|
||||
if (interfaceClass == null) {
|
||||
|
||||
Class<?>[] allInterfaces = annotatedServiceBeanClass.getInterfaces();
|
||||
// Find all interfaces from the annotated class
|
||||
// To resolve an issue : https://github.com/apache/incubator-dubbo/issues/3251
|
||||
Class<?>[] allInterfaces = ClassUtils.getAllInterfacesForClass(annotatedServiceBeanClass);
|
||||
|
||||
if (allInterfaces.length > 0) {
|
||||
interfaceClass = allInterfaces[0];
|
||||
|
|
@ -435,11 +436,6 @@ public class ServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistr
|
|||
builder.addPropertyValue("protocols", protocolRuntimeBeanReferences);
|
||||
}
|
||||
|
||||
List<MethodConfig> methodConfigs = MethodConfig.constructMethodConfig(service.methods());
|
||||
if (!methodConfigs.isEmpty()) {
|
||||
builder.addPropertyValue("methods", methodConfigs);
|
||||
}
|
||||
|
||||
return builder.getBeanDefinition();
|
||||
|
||||
}
|
||||
|
|
@ -490,4 +486,4 @@ public class ServiceAnnotationBeanPostProcessor implements BeanDefinitionRegistr
|
|||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,113 +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.apache.dubbo.config.spring.beans.factory.annotation;
|
||||
|
||||
import org.apache.dubbo.config.annotation.Reference;
|
||||
import org.apache.dubbo.config.annotation.Service;
|
||||
import org.apache.dubbo.config.spring.ReferenceBean;
|
||||
import org.apache.dubbo.config.spring.ServiceBean;
|
||||
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.apache.dubbo.config.spring.util.AnnotationUtils.resolveInterfaceName;
|
||||
|
||||
|
||||
/**
|
||||
* Dubbo {@link Service @Service} Bean Builder
|
||||
*
|
||||
* @see Service
|
||||
* @see Reference
|
||||
* @see ServiceBean
|
||||
* @see ReferenceBean
|
||||
* @since 2.6.5
|
||||
*/
|
||||
class ServiceBeanNameBuilder {
|
||||
|
||||
private static final String SEPARATOR = ":";
|
||||
|
||||
private final String interfaceClassName;
|
||||
|
||||
private final Environment environment;
|
||||
|
||||
// Optional
|
||||
private String version;
|
||||
|
||||
private String group;
|
||||
|
||||
private ServiceBeanNameBuilder(String interfaceClassName, Environment environment) {
|
||||
this.interfaceClassName = interfaceClassName;
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
private ServiceBeanNameBuilder(Class<?> interfaceClass, Environment environment) {
|
||||
this(interfaceClass.getName(), environment);
|
||||
}
|
||||
|
||||
private ServiceBeanNameBuilder(Service service, Class<?> interfaceClass, Environment environment) {
|
||||
this(resolveInterfaceName(service, interfaceClass), environment);
|
||||
this.group(service.group());
|
||||
this.version(service.version());
|
||||
}
|
||||
|
||||
private ServiceBeanNameBuilder(Reference reference, Class<?> interfaceClass, Environment environment) {
|
||||
this(resolveInterfaceName(reference, interfaceClass), environment);
|
||||
this.group(reference.group());
|
||||
this.version(reference.version());
|
||||
}
|
||||
|
||||
public static ServiceBeanNameBuilder create(Class<?> interfaceClass, Environment environment) {
|
||||
return new ServiceBeanNameBuilder(interfaceClass, environment);
|
||||
}
|
||||
|
||||
public static ServiceBeanNameBuilder create(Service service, Class<?> interfaceClass, Environment environment) {
|
||||
return new ServiceBeanNameBuilder(service, interfaceClass, environment);
|
||||
}
|
||||
|
||||
public static ServiceBeanNameBuilder create(Reference reference, Class<?> interfaceClass, Environment environment) {
|
||||
return new ServiceBeanNameBuilder(reference, interfaceClass, environment);
|
||||
}
|
||||
|
||||
private static void append(StringBuilder builder, String value) {
|
||||
if (StringUtils.hasText(value)) {
|
||||
builder.append(SEPARATOR).append(value);
|
||||
}
|
||||
}
|
||||
|
||||
public ServiceBeanNameBuilder group(String group) {
|
||||
this.group = group;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ServiceBeanNameBuilder version(String version) {
|
||||
this.version = version;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String build() {
|
||||
StringBuilder beanNameBuilder = new StringBuilder("ServiceBean");
|
||||
// Required
|
||||
append(beanNameBuilder, interfaceClassName);
|
||||
// Optional
|
||||
append(beanNameBuilder, version);
|
||||
append(beanNameBuilder, group);
|
||||
// Build
|
||||
String rawBeanName = beanNameBuilder.toString();
|
||||
// Resolve placeholders
|
||||
return environment.resolvePlaceholders(rawBeanName);
|
||||
}
|
||||
}
|
||||
|
|
@ -16,29 +16,31 @@
|
|||
*/
|
||||
package org.apache.dubbo.config.spring.beans.factory.annotation;
|
||||
|
||||
|
||||
import org.apache.dubbo.config.annotation.Reference;
|
||||
import org.apache.dubbo.config.annotation.Service;
|
||||
import org.apache.dubbo.config.spring.api.DemoService;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.mock.env.MockEnvironment;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static org.apache.dubbo.config.spring.beans.factory.annotation.AnnotationBeanNameBuilderTest.GROUP;
|
||||
import static org.apache.dubbo.config.spring.beans.factory.annotation.AnnotationBeanNameBuilderTest.VERSION;
|
||||
|
||||
/**
|
||||
* {@link ServiceBeanNameBuilder} Test
|
||||
* {@link AnnotationBeanNameBuilder} Test
|
||||
*
|
||||
* @see ServiceBeanNameBuilder
|
||||
* @since 2.6.5
|
||||
* @see AnnotationBeanNameBuilder
|
||||
* @since 2.6.6
|
||||
*/
|
||||
@Service(interfaceClass = DemoService.class, group = ServiceBeanNameBuilderTest.GROUP, version = ServiceBeanNameBuilderTest.VERSION,
|
||||
@Service(interfaceClass = DemoService.class, group = GROUP, version = VERSION,
|
||||
application = "application", module = "module", registry = {"1", "2", "3"})
|
||||
public class ServiceBeanNameBuilderTest {
|
||||
public class AnnotationBeanNameBuilderTest {
|
||||
|
||||
@Reference(interfaceClass = DemoService.class, group = "DUBBO", version = "1.0.0",
|
||||
@Reference(interfaceClass = DemoService.class, group = "DUBBO", version = "${dubbo.version}",
|
||||
application = "application", module = "module", registry = {"1", "2", "3"})
|
||||
static final Class<?> INTERFACE_CLASS = DemoService.class;
|
||||
|
||||
|
|
@ -46,30 +48,36 @@ public class ServiceBeanNameBuilderTest {
|
|||
|
||||
static final String VERSION = "1.0.0";
|
||||
|
||||
static final String BEAN_NAME = "ServiceBean:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO";
|
||||
private MockEnvironment environment;
|
||||
|
||||
private MockEnvironment environment = new MockEnvironment();
|
||||
|
||||
@Test
|
||||
public void testRequiredAttributes() {
|
||||
ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(INTERFACE_CLASS, environment);
|
||||
Assertions.assertEquals("ServiceBean:org.apache.dubbo.config.spring.api.DemoService", builder.build());
|
||||
@Before
|
||||
public void prepare() {
|
||||
environment = new MockEnvironment();
|
||||
environment.setProperty("dubbo.version", "1.0.0");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServiceAnnotation() {
|
||||
Service service = AnnotationUtils.getAnnotation(ServiceBeanNameBuilderTest.class, Service.class);
|
||||
ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(service, INTERFACE_CLASS, environment);
|
||||
Assertions.assertEquals(BEAN_NAME,
|
||||
Service service = AnnotationUtils.getAnnotation(AnnotationBeanNameBuilderTest.class, Service.class);
|
||||
AnnotationBeanNameBuilder builder = AnnotationBeanNameBuilder.create(service, INTERFACE_CLASS);
|
||||
Assert.assertEquals("providers:dubbo:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO",
|
||||
builder.build());
|
||||
|
||||
builder.environment(environment);
|
||||
Assert.assertEquals("providers:dubbo:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO",
|
||||
builder.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReferenceAnnotation() {
|
||||
Reference reference = AnnotationUtils.getAnnotation(ReflectionUtils.findField(ServiceBeanNameBuilderTest.class, "INTERFACE_CLASS"), Reference.class);
|
||||
ServiceBeanNameBuilder builder = ServiceBeanNameBuilder.create(reference, INTERFACE_CLASS, environment);
|
||||
Assertions.assertEquals(BEAN_NAME,
|
||||
Reference reference = AnnotationUtils.getAnnotation(ReflectionUtils.findField(AnnotationBeanNameBuilderTest.class, "INTERFACE_CLASS"), Reference.class);
|
||||
AnnotationBeanNameBuilder builder = AnnotationBeanNameBuilder.create(reference, INTERFACE_CLASS);
|
||||
Assert.assertEquals("consumers:dubbo:org.apache.dubbo.config.spring.api.DemoService:${dubbo.version}:DUBBO",
|
||||
builder.build());
|
||||
|
||||
builder.environment(environment);
|
||||
Assert.assertEquals("consumers:dubbo:org.apache.dubbo.config.spring.api.DemoService:1.0.0:DUBBO",
|
||||
builder.build());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,153 @@
|
|||
<!--
|
||||
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.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-registry</artifactId>
|
||||
<version>2.7.1-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-registry-nacos</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>The Nacos registry module of Dubbo project</description>
|
||||
|
||||
<properties>
|
||||
<nacos.version>0.9.0</nacos.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-registry-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.nacos</groupId>
|
||||
<artifactId>nacos-client</artifactId>
|
||||
<version>${nacos.version}</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Test Libraries -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-config-api</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-serialization-hessian2</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-config-spring</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-rpc-dubbo</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-remoting-netty4</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>ch.qos.logback</groupId>
|
||||
<artifactId>logback-classic</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- REST support dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.apache.dubbo</groupId>
|
||||
<artifactId>dubbo-rpc-rest</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jaxrs</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-client</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-netty4</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jackson-provider</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.resteasy</groupId>
|
||||
<artifactId>resteasy-jaxb-provider</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
@ -0,0 +1,530 @@
|
|||
/*
|
||||
* 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.apache.dubbo.registry.nacos;
|
||||
|
||||
import org.apache.dubbo.common.Constants;
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.common.logger.Logger;
|
||||
import org.apache.dubbo.common.logger.LoggerFactory;
|
||||
import org.apache.dubbo.common.utils.UrlUtils;
|
||||
import org.apache.dubbo.registry.NotifyListener;
|
||||
import org.apache.dubbo.registry.Registry;
|
||||
import org.apache.dubbo.registry.support.FailbackRegistry;
|
||||
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.api.naming.NamingService;
|
||||
import com.alibaba.nacos.api.naming.listener.Event;
|
||||
import com.alibaba.nacos.api.naming.listener.EventListener;
|
||||
import com.alibaba.nacos.api.naming.listener.NamingEvent;
|
||||
import com.alibaba.nacos.api.naming.pojo.Instance;
|
||||
import com.alibaba.nacos.api.naming.pojo.ListView;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static org.apache.dubbo.common.Constants.ADMIN_PROTOCOL;
|
||||
import static org.apache.dubbo.common.Constants.CATEGORY_KEY;
|
||||
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.DEFAULT_CATEGORY;
|
||||
import static org.apache.dubbo.common.Constants.GROUP_KEY;
|
||||
import static org.apache.dubbo.common.Constants.INTERFACE_KEY;
|
||||
import static org.apache.dubbo.common.Constants.PROVIDERS_CATEGORY;
|
||||
import static org.apache.dubbo.common.Constants.ROUTERS_CATEGORY;
|
||||
import static org.apache.dubbo.common.Constants.VERSION_KEY;
|
||||
|
||||
/**
|
||||
* Nacos {@link Registry}
|
||||
*
|
||||
* @see #SERVICE_NAME_SEPARATOR
|
||||
* @see #PAGINATION_SIZE
|
||||
* @see #LOOKUP_INTERVAL
|
||||
* @since 2.6.5
|
||||
*/
|
||||
public class NacosRegistry extends FailbackRegistry {
|
||||
|
||||
/**
|
||||
* All supported categories
|
||||
*/
|
||||
private static final String[] ALL_SUPPORTED_CATEGORIES = of(
|
||||
PROVIDERS_CATEGORY,
|
||||
CONSUMERS_CATEGORY,
|
||||
ROUTERS_CATEGORY,
|
||||
CONFIGURATORS_CATEGORY
|
||||
);
|
||||
|
||||
private static final int CATEGORY_INDEX = 0;
|
||||
|
||||
private static final int SERVICE_INTERFACE_INDEX = 1;
|
||||
|
||||
private static final int SERVICE_VERSION_INDEX = 2;
|
||||
|
||||
private static final int SERVICE_GROUP_INDEX = 3;
|
||||
|
||||
private static final String WILDCARD = "*";
|
||||
|
||||
/**
|
||||
* The separator for service name
|
||||
*
|
||||
* @revert change a constant to be configurable, it's designed for Windows file name that is compatible with old
|
||||
* Nacos binary release(< 0.6.1)
|
||||
*/
|
||||
private static final String SERVICE_NAME_SEPARATOR = System.getProperty("nacos.service.name.separator", ":");
|
||||
|
||||
/**
|
||||
* The pagination size of query for Nacos service names(only for Dubbo-OPS)
|
||||
*/
|
||||
private static final int PAGINATION_SIZE = Integer.getInteger("nacos.service.names.pagination.size", 100);
|
||||
|
||||
/**
|
||||
* The interval in second of lookup Nacos service names(only for Dubbo-OPS)
|
||||
*/
|
||||
private static final long LOOKUP_INTERVAL = Long.getLong("nacos.service.names.lookup.interval", 30);
|
||||
|
||||
/**
|
||||
* {@link ScheduledExecutorService} lookup Nacos service names(only for Dubbo-OPS)
|
||||
*/
|
||||
private volatile ScheduledExecutorService scheduledExecutorService;
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
private final NamingService namingService;
|
||||
|
||||
private final ConcurrentMap<String, EventListener> nacosListeners;
|
||||
|
||||
public NacosRegistry(URL url, NamingService namingService) {
|
||||
super(url);
|
||||
this.namingService = namingService;
|
||||
this.nacosListeners = new ConcurrentHashMap<String, EventListener>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable() {
|
||||
return "UP".equals(namingService.getServerStatus());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<URL> lookup(final URL url) {
|
||||
final List<URL> urls = new LinkedList<URL>();
|
||||
execute(new NamingServiceCallback() {
|
||||
@Override
|
||||
public void callback(NamingService namingService) throws NacosException {
|
||||
List<String> serviceNames = getServiceNames(url, null);
|
||||
for (String serviceName : serviceNames) {
|
||||
List<Instance> instances = namingService.getAllInstances(serviceName);
|
||||
urls.addAll(buildURLs(url, instances));
|
||||
}
|
||||
}
|
||||
});
|
||||
return urls;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doRegister(URL url) {
|
||||
final String serviceName = getServiceName(url);
|
||||
final Instance instance = createInstance(url);
|
||||
execute(new NamingServiceCallback() {
|
||||
public void callback(NamingService namingService) throws NacosException {
|
||||
namingService.registerInstance(serviceName, instance);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doUnregister(final URL url) {
|
||||
execute(new NamingServiceCallback() {
|
||||
public void callback(NamingService namingService) throws NacosException {
|
||||
String serviceName = getServiceName(url);
|
||||
Instance instance = createInstance(url);
|
||||
namingService.deregisterInstance(serviceName, instance.getIp(), instance.getPort());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doSubscribe(final URL url, final NotifyListener listener) {
|
||||
List<String> serviceNames = getServiceNames(url, listener);
|
||||
doSubscribe(url, listener, serviceNames);
|
||||
}
|
||||
|
||||
private void doSubscribe(final URL url, final NotifyListener listener, final List<String> serviceNames) {
|
||||
execute(new NamingServiceCallback() {
|
||||
@Override
|
||||
public void callback(NamingService namingService) throws NacosException {
|
||||
for (String serviceName : serviceNames) {
|
||||
List<Instance> instances = namingService.getAllInstances(serviceName);
|
||||
notifySubscriber(url, listener, instances);
|
||||
subscribeEventListener(serviceName, url, listener);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doUnsubscribe(URL url, NotifyListener listener) {
|
||||
if (isAdminProtocol(url)) {
|
||||
shutdownServiceNamesLookup();
|
||||
}
|
||||
}
|
||||
|
||||
private void shutdownServiceNamesLookup() {
|
||||
if (scheduledExecutorService != null) {
|
||||
scheduledExecutorService.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the service names from the specified {@link URL url}
|
||||
*
|
||||
* @param url {@link URL}
|
||||
* @param listener {@link NotifyListener}
|
||||
* @return non-null
|
||||
*/
|
||||
private List<String> getServiceNames(URL url, NotifyListener listener) {
|
||||
if (isAdminProtocol(url)) {
|
||||
scheduleServiceNamesLookup(url, listener);
|
||||
return getServiceNamesForOps(url);
|
||||
} else {
|
||||
return doGetServiceNames(url);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isAdminProtocol(URL url) {
|
||||
return ADMIN_PROTOCOL.equals(url.getProtocol());
|
||||
}
|
||||
|
||||
private void scheduleServiceNamesLookup(final URL url, final NotifyListener listener) {
|
||||
if (scheduledExecutorService == null) {
|
||||
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
|
||||
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<String> serviceNames = getAllServiceNames();
|
||||
filterData(serviceNames, new NacosDataFilter<String>() {
|
||||
@Override
|
||||
public boolean accept(String serviceName) {
|
||||
boolean accepted = false;
|
||||
for (String category : ALL_SUPPORTED_CATEGORIES) {
|
||||
String prefix = category + SERVICE_NAME_SEPARATOR;
|
||||
if (StringUtils.startsWith(serviceName, prefix)) {
|
||||
accepted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return accepted;
|
||||
}
|
||||
});
|
||||
doSubscribe(url, listener, serviceNames);
|
||||
}
|
||||
}, LOOKUP_INTERVAL, LOOKUP_INTERVAL, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the service names for Dubbo OPS
|
||||
*
|
||||
* @param url {@link URL}
|
||||
* @return non-null
|
||||
*/
|
||||
private List<String> getServiceNamesForOps(URL url) {
|
||||
List<String> serviceNames = getAllServiceNames();
|
||||
filterServiceNames(serviceNames, url);
|
||||
return serviceNames;
|
||||
}
|
||||
|
||||
private List<String> getAllServiceNames() {
|
||||
|
||||
final List<String> serviceNames = new LinkedList<String>();
|
||||
|
||||
execute(new NamingServiceCallback() {
|
||||
@Override
|
||||
public void callback(NamingService namingService) throws NacosException {
|
||||
|
||||
int pageIndex = 1;
|
||||
ListView<String> listView = namingService.getServicesOfServer(pageIndex, PAGINATION_SIZE);
|
||||
// First page data
|
||||
List<String> firstPageData = listView.getData();
|
||||
// Append first page into list
|
||||
serviceNames.addAll(firstPageData);
|
||||
// the total count
|
||||
int count = listView.getCount();
|
||||
// the number of pages
|
||||
int pageNumbers = count / PAGINATION_SIZE;
|
||||
int remainder = count % PAGINATION_SIZE;
|
||||
// remain
|
||||
if (remainder > 0) {
|
||||
pageNumbers += 1;
|
||||
}
|
||||
// If more than 1 page
|
||||
while (pageIndex < pageNumbers) {
|
||||
listView = namingService.getServicesOfServer(++pageIndex, PAGINATION_SIZE);
|
||||
serviceNames.addAll(listView.getData());
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
return serviceNames;
|
||||
}
|
||||
|
||||
private void filterServiceNames(List<String> serviceNames, URL url) {
|
||||
|
||||
final String[] categories = getCategories(url);
|
||||
|
||||
final String targetServiceInterface = url.getServiceInterface();
|
||||
|
||||
final String targetVersion = url.getParameter(VERSION_KEY);
|
||||
|
||||
final String targetGroup = url.getParameter(GROUP_KEY);
|
||||
|
||||
filterData(serviceNames, new NacosDataFilter<String>() {
|
||||
@Override
|
||||
public boolean accept(String serviceName) {
|
||||
// split service name to segments
|
||||
// (required) segments[0] = category
|
||||
// (required) segments[1] = serviceInterface
|
||||
// (required) segments[2] = version
|
||||
// (optional) segments[3] = group
|
||||
String[] segments = StringUtils.split(serviceName, SERVICE_NAME_SEPARATOR);
|
||||
int length = segments.length;
|
||||
if (length < 3) { // must present 3 segments or more
|
||||
return false;
|
||||
}
|
||||
|
||||
String category = segments[CATEGORY_INDEX];
|
||||
if (!ArrayUtils.contains(categories, category)) { // no match category
|
||||
return false;
|
||||
}
|
||||
|
||||
String serviceInterface = segments[SERVICE_INTERFACE_INDEX];
|
||||
if (!WILDCARD.equals(targetServiceInterface) &&
|
||||
!StringUtils.equals(targetServiceInterface, serviceInterface)) { // no match service interface
|
||||
return false;
|
||||
}
|
||||
|
||||
String version = segments[SERVICE_VERSION_INDEX];
|
||||
if (!WILDCARD.equals(targetVersion) &&
|
||||
!StringUtils.equals(targetVersion, version)) { // no match service version
|
||||
return false;
|
||||
}
|
||||
|
||||
String group = length > 3 ? segments[SERVICE_GROUP_INDEX] : null;
|
||||
if (group != null && !WILDCARD.equals(targetGroup)
|
||||
&& !StringUtils.equals(targetGroup, group)) { // no match service group
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private <T> void filterData(Collection<T> collection, NacosDataFilter<T> filter) {
|
||||
Iterator<T> iterator = collection.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
T data = iterator.next();
|
||||
if (!filter.accept(data)) { // remove if not accept
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> doGetServiceNames(URL url) {
|
||||
String[] categories = getCategories(url);
|
||||
List<String> serviceNames = new ArrayList<String>(categories.length);
|
||||
for (String category : categories) {
|
||||
final String serviceName = getServiceName(url, category);
|
||||
serviceNames.add(serviceName);
|
||||
}
|
||||
return serviceNames;
|
||||
}
|
||||
|
||||
private List<URL> buildURLs(URL consumerURL, Collection<Instance> instances) {
|
||||
if (instances.isEmpty()) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
List<URL> urls = new LinkedList<URL>();
|
||||
for (Instance instance : instances) {
|
||||
URL url = buildURL(instance);
|
||||
if (UrlUtils.isMatch(consumerURL, url)) {
|
||||
urls.add(url);
|
||||
}
|
||||
}
|
||||
return urls;
|
||||
}
|
||||
|
||||
private void subscribeEventListener(String serviceName, final URL url, final NotifyListener listener)
|
||||
throws NacosException {
|
||||
if (!nacosListeners.containsKey(serviceName)) {
|
||||
EventListener eventListener = new EventListener() {
|
||||
public void onEvent(Event event) {
|
||||
if (event instanceof NamingEvent) {
|
||||
NamingEvent e = (NamingEvent) event;
|
||||
notifySubscriber(url, listener, e.getInstances());
|
||||
}
|
||||
}
|
||||
};
|
||||
namingService.subscribe(serviceName, eventListener);
|
||||
nacosListeners.put(serviceName, eventListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify the Healthy {@link Instance instances} to subscriber.
|
||||
*
|
||||
* @param url {@link URL}
|
||||
* @param listener {@link NotifyListener}
|
||||
* @param instances all {@link Instance instances}
|
||||
*/
|
||||
private void notifySubscriber(URL url, NotifyListener listener, Collection<Instance> instances) {
|
||||
List<Instance> healthyInstances = new LinkedList<Instance>(instances);
|
||||
// Healthy Instances
|
||||
filterHealthyInstances(healthyInstances);
|
||||
List<URL> urls = buildURLs(url, healthyInstances);
|
||||
NacosRegistry.this.notify(url, listener, urls);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the categories from {@link URL}
|
||||
*
|
||||
* @param url {@link URL}
|
||||
* @return non-null array
|
||||
*/
|
||||
private String[] getCategories(URL url) {
|
||||
return Constants.ANY_VALUE.equals(url.getServiceInterface()) ?
|
||||
ALL_SUPPORTED_CATEGORIES : of(Constants.DEFAULT_CATEGORY);
|
||||
}
|
||||
|
||||
private URL buildURL(Instance instance) {
|
||||
Map<String, String> metadata = instance.getMetadata();
|
||||
String protocol = metadata.get(Constants.PROTOCOL_KEY);
|
||||
String path = metadata.get(Constants.PATH_KEY);
|
||||
URL url = new URL(protocol,
|
||||
instance.getIp(),
|
||||
instance.getPort(),
|
||||
path,
|
||||
instance.getMetadata());
|
||||
return url;
|
||||
}
|
||||
|
||||
private Instance createInstance(URL url) {
|
||||
// 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());
|
||||
newURL = newURL.addParameter(Constants.PATH_KEY, url.getPath());
|
||||
String ip = url.getHost();
|
||||
int port = url.getPort();
|
||||
Instance instance = new Instance();
|
||||
instance.setIp(ip);
|
||||
instance.setPort(port);
|
||||
instance.setMetadata(new HashMap<String, String>(newURL.getParameters()));
|
||||
return instance;
|
||||
}
|
||||
|
||||
private String getServiceName(URL url) {
|
||||
String category = url.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY);
|
||||
return getServiceName(url, category);
|
||||
}
|
||||
|
||||
private String getServiceName(URL url, String category) {
|
||||
StringBuilder serviceNameBuilder = new StringBuilder(category);
|
||||
appendIfPresent(serviceNameBuilder, url, INTERFACE_KEY);
|
||||
appendIfPresent(serviceNameBuilder, url, VERSION_KEY);
|
||||
appendIfPresent(serviceNameBuilder, url, GROUP_KEY);
|
||||
return serviceNameBuilder.toString();
|
||||
}
|
||||
|
||||
private void appendIfPresent(StringBuilder target, URL url, String parameterName) {
|
||||
String parameterValue = url.getParameter(parameterName);
|
||||
if (!StringUtils.isBlank(parameterValue)) {
|
||||
target.append(SERVICE_NAME_SEPARATOR).append(parameterValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void execute(NamingServiceCallback callback) {
|
||||
try {
|
||||
callback.callback(namingService);
|
||||
} catch (NacosException e) {
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error(e.getErrMsg(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void filterHealthyInstances(Collection<Instance> instances) {
|
||||
filterData(instances, new NacosDataFilter<Instance>() {
|
||||
@Override
|
||||
public boolean accept(Instance data) {
|
||||
return data.isEnabled();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static <T> T[] of(T... values) {
|
||||
return values;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* A filter for Nacos data
|
||||
*
|
||||
* @since 2.6.5
|
||||
*/
|
||||
private interface NacosDataFilter<T> {
|
||||
|
||||
/**
|
||||
* Tests whether or not the specified data should be accepted.
|
||||
*
|
||||
* @param data The data to be tested
|
||||
* @return <code>true</code> if and only if <code>data</code>
|
||||
* should be accepted
|
||||
*/
|
||||
boolean accept(T data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link NamingService} Callback
|
||||
*
|
||||
* @since 2.6.5
|
||||
*/
|
||||
interface NamingServiceCallback {
|
||||
|
||||
/**
|
||||
* Callback
|
||||
*
|
||||
* @param namingService {@link NamingService}
|
||||
* @throws NacosException
|
||||
*/
|
||||
void callback(NamingService namingService) throws NacosException;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* 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.apache.dubbo.registry.nacos;
|
||||
|
||||
import org.apache.dubbo.common.URL;
|
||||
import org.apache.dubbo.registry.Registry;
|
||||
import org.apache.dubbo.registry.RegistryFactory;
|
||||
import org.apache.dubbo.registry.support.AbstractRegistryFactory;
|
||||
|
||||
import com.alibaba.nacos.api.NacosFactory;
|
||||
import com.alibaba.nacos.api.exception.NacosException;
|
||||
import com.alibaba.nacos.api.naming.NamingService;
|
||||
import com.alibaba.nacos.client.naming.utils.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import static com.alibaba.nacos.api.PropertyKeyConst.ACCESS_KEY;
|
||||
import static com.alibaba.nacos.api.PropertyKeyConst.CLUSTER_NAME;
|
||||
import static com.alibaba.nacos.api.PropertyKeyConst.ENDPOINT;
|
||||
import static com.alibaba.nacos.api.PropertyKeyConst.NAMESPACE;
|
||||
import static com.alibaba.nacos.api.PropertyKeyConst.SECRET_KEY;
|
||||
import static com.alibaba.nacos.api.PropertyKeyConst.SERVER_ADDR;
|
||||
import static com.alibaba.nacos.client.naming.utils.UtilAndComs.NACOS_NAMING_LOG_NAME;
|
||||
import static org.apache.dubbo.common.Constants.BACKUP_KEY;
|
||||
|
||||
/**
|
||||
* Nacos {@link RegistryFactory}
|
||||
*
|
||||
* @since 2.6.5
|
||||
*/
|
||||
public class NacosRegistryFactory extends AbstractRegistryFactory {
|
||||
|
||||
private final Logger logger = LoggerFactory.getLogger(getClass());
|
||||
|
||||
protected Registry createRegistry(URL url) {
|
||||
return new NacosRegistry(url, buildNamingService(url));
|
||||
}
|
||||
|
||||
private NamingService buildNamingService(URL url) {
|
||||
Properties nacosProperties = buildNacosProperties(url);
|
||||
NamingService namingService = null;
|
||||
try {
|
||||
namingService = NacosFactory.createNamingService(nacosProperties);
|
||||
} catch (NacosException e) {
|
||||
if (logger.isErrorEnabled()) {
|
||||
logger.error(e.getErrMsg(), e);
|
||||
}
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
return namingService;
|
||||
}
|
||||
|
||||
private Properties buildNacosProperties(URL url) {
|
||||
Properties properties = new Properties();
|
||||
setServerAddr(url, properties);
|
||||
setProperties(url, properties);
|
||||
return properties;
|
||||
}
|
||||
|
||||
private void setServerAddr(URL url, Properties properties) {
|
||||
StringBuilder serverAddrBuilder =
|
||||
new StringBuilder(url.getHost()) // Host
|
||||
.append(":")
|
||||
.append(url.getPort()); // Port
|
||||
|
||||
// Append backup parameter as other servers
|
||||
String backup = url.getParameter(BACKUP_KEY);
|
||||
if (backup != null) {
|
||||
serverAddrBuilder.append(",").append(backup);
|
||||
}
|
||||
|
||||
String serverAddr = serverAddrBuilder.toString();
|
||||
properties.put(SERVER_ADDR, serverAddr);
|
||||
}
|
||||
|
||||
private void setProperties(URL url, Properties properties) {
|
||||
putPropertyIfAbsent(url, properties, NAMESPACE);
|
||||
putPropertyIfAbsent(url, properties, NACOS_NAMING_LOG_NAME);
|
||||
putPropertyIfAbsent(url, properties, ENDPOINT);
|
||||
putPropertyIfAbsent(url, properties, NAMESPACE);
|
||||
putPropertyIfAbsent(url, properties, ACCESS_KEY);
|
||||
putPropertyIfAbsent(url, properties, SECRET_KEY);
|
||||
putPropertyIfAbsent(url, properties, CLUSTER_NAME);
|
||||
}
|
||||
|
||||
private void putPropertyIfAbsent(URL url, Properties properties, String propertyName) {
|
||||
String propertyValue = url.getParameter(propertyName);
|
||||
if (StringUtils.isNotEmpty(propertyValue)) {
|
||||
properties.setProperty(propertyName, propertyValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
nacos=org.apache.dubbo.registry.nacos.NacosRegistryFactory
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* 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.apache.dubbo.demo.consumer;
|
||||
|
||||
import org.apache.dubbo.config.annotation.Reference;
|
||||
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
|
||||
import org.apache.dubbo.demo.service.DemoService;
|
||||
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* {@link DemoService} consumer demo
|
||||
*/
|
||||
@EnableDubbo
|
||||
@PropertySource(value = "classpath:/consumer-config.properties")
|
||||
public class DemoServiceConsumerBootstrap {
|
||||
|
||||
@Reference(version = "${demo.service.version}")
|
||||
private DemoService demoService;
|
||||
|
||||
@Reference(version = "${demo.service.version}", protocol = "rest")
|
||||
private DemoService restDemoService;
|
||||
|
||||
@PostConstruct
|
||||
public void init() throws InterruptedException {
|
||||
for (int j = 0; j < 10; j++) {
|
||||
System.out.println(demoService.sayName("小马哥(mercyblitz)"));
|
||||
System.out.println(restDemoService.sayName("小马哥(mercyblitz)"));
|
||||
}
|
||||
Thread.sleep(TimeUnit.SECONDS.toMillis(5));
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.register(DemoServiceConsumerBootstrap.class);
|
||||
context.refresh();
|
||||
System.in.read();
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* 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.apache.dubbo.demo.consumer;
|
||||
|
||||
import org.apache.dubbo.demo.service.DemoService;
|
||||
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* {@link DemoService} consumer demo XML bootstrap
|
||||
*/
|
||||
public class DemoServiceConsumerXmlBootstrap {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
|
||||
context.setConfigLocation("/META-INF/spring/dubbo-consumer-context.xml");
|
||||
context.refresh();
|
||||
System.out.println("DemoService consumer (XML) is starting...");
|
||||
DemoService demoService = context.getBean("demoService", DemoService.class);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
System.out.println(demoService.sayName("小马哥(mercyblitz)"));
|
||||
}
|
||||
System.in.read();
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
* 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.apache.dubbo.demo.provider;
|
||||
|
||||
import org.apache.dubbo.config.spring.context.annotation.EnableDubbo;
|
||||
import org.apache.dubbo.demo.service.DemoService;
|
||||
|
||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
||||
import org.springframework.context.annotation.PropertySource;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* {@link DemoService} provider demo
|
||||
*/
|
||||
@EnableDubbo(scanBasePackages = "org.apache.dubbo.demo.service")
|
||||
@PropertySource(value = "classpath:/provider-config.properties")
|
||||
public class DemoServiceProviderBootstrap {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
|
||||
context.register(DemoServiceProviderBootstrap.class);
|
||||
context.refresh();
|
||||
System.out.println("DemoService provider is starting...");
|
||||
System.in.read();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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.apache.dubbo.demo.provider;
|
||||
|
||||
import org.apache.dubbo.demo.service.DemoService;
|
||||
|
||||
import org.springframework.context.support.ClassPathXmlApplicationContext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* {@link DemoService} provider demo XML bootstrap
|
||||
*/
|
||||
public class DemoServiceProviderXmlBootstrap {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext();
|
||||
context.setConfigLocation("/META-INF/spring/dubbo-provider-context.xml");
|
||||
context.refresh();
|
||||
System.out.println("DemoService provider (XML) is starting...");
|
||||
System.in.read();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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.apache.dubbo.demo.service;
|
||||
|
||||
import org.apache.dubbo.config.annotation.Service;
|
||||
import org.apache.dubbo.rpc.RpcContext;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
|
||||
|
||||
/**
|
||||
* Default {@link DemoService}
|
||||
*
|
||||
* @since 2.6.5
|
||||
*/
|
||||
@Service(version = "${demo.service.version}")
|
||||
public class DefaultService implements DemoService {
|
||||
|
||||
@Value("${demo.service.name}")
|
||||
private String serviceName;
|
||||
|
||||
public String sayName(String name) {
|
||||
RpcContext rpcContext = RpcContext.getContext();
|
||||
return String.format("Service [name :%s , protocol: %s , port : %d] %s(\"%s\") : Hello,%s",
|
||||
serviceName,
|
||||
rpcContext.getUrl().getProtocol(),
|
||||
rpcContext.getLocalPort(),
|
||||
rpcContext.getMethodName(),
|
||||
name,
|
||||
name);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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.apache.dubbo.demo.service;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.QueryParam;
|
||||
|
||||
/**
|
||||
* DemoService
|
||||
*
|
||||
* @since 2.6.5
|
||||
*/
|
||||
@Path("/demo-service")
|
||||
public interface DemoService {
|
||||
|
||||
@GET
|
||||
String sayName(@QueryParam("name") String name);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
|
||||
|
||||
<dubbo:application name="dubbo-consumer-xml-demo"/>
|
||||
|
||||
<!-- Nacos Registry -->
|
||||
<dubbo:registry address="nacos://127.0.0.1:8848"/>
|
||||
|
||||
<!-- Reference interface -->
|
||||
<dubbo:reference id="demoService" interface="org.apache.dubbo.demo.service.DemoService" version="2.0.0"/>
|
||||
|
||||
</beans>
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd">
|
||||
|
||||
<dubbo:application name="dubbo-provider-xml-demo"/>
|
||||
|
||||
<!-- Nacos Registry -->
|
||||
<dubbo:registry address="nacos://127.0.0.1:8848"/>
|
||||
|
||||
<!-- Use random port as Dubbo -->
|
||||
<dubbo:protocol name="dubbo" port="-1"/>
|
||||
|
||||
<dubbo:service interface="org.apache.dubbo.demo.service.DemoService" ref="demoService" version="2.0.0"/>
|
||||
|
||||
<bean id="demoService" class="org.apache.dubbo.demo.service.DefaultService"/>
|
||||
</beans>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
## Dubbo Application info
|
||||
dubbo.application.name=dubbo-consumer-demo
|
||||
## Nacos registry address
|
||||
dubbo.registry.address=nacos://127.0.0.1:8848
|
||||
# @Reference version
|
||||
demo.service.version=1.0.0
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
## Dubbo Application info
|
||||
dubbo.application.name=dubbo-provider-demo
|
||||
## Nacos registry address
|
||||
dubbo.registry.protocol=nacos
|
||||
dubbo.registry.address=127.0.0.1:8848
|
||||
## Exports multiple protocols
|
||||
### Dubbo Protocol using random port
|
||||
dubbo.protocols.dubbo.port=-1
|
||||
### REST protocol
|
||||
dubbo.protocols.rest.port=9090
|
||||
dubbo.protocols.rest.server=netty
|
||||
# Provider @Service info
|
||||
demo.service.version=1.0.0
|
||||
demo.service.name=demoService
|
||||
|
|
@ -36,5 +36,6 @@
|
|||
<module>dubbo-registry-redis</module>
|
||||
<module>dubbo-registry-consul</module>
|
||||
<module>dubbo-registry-etcd3</module>
|
||||
<module>dubbo-registry-nacos</module>
|
||||
</modules>
|
||||
</project>
|
||||
|
|
|
|||
Loading…
Reference in New Issue