You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dubbo.apache.org by li...@apache.org on 2021/01/15 15:48:45 UTC
[dubbo] branch 3.0 updated: merge k8s feature (#7104)
This is an automated email from the ASF dual-hosted git repository.
liujun pushed a commit to branch 3.0
in repository https://gitbox.apache.org/repos/asf/dubbo.git
The following commit(s) were added to refs/heads/3.0 by this push:
new 43e5eda merge k8s feature (#7104)
43e5eda is described below
commit 43e5edaefec52df4653f0af4cb034165fdac4b3d
Author: ken.lj <ke...@gmail.com>
AuthorDate: Fri Jan 15 23:48:28 2021 +0800
merge k8s feature (#7104)
---
.../support/wrapper/MockClusterInvokerTest.java | 42 ++-
.../wrapper/MockProviderRpcExceptionTest.java | 8 +-
.../dubbo/common/constants/CommonConstants.java | 6 +
.../org/apache/dubbo/config/ApplicationConfig.java | 50 +++
.../org/apache/dubbo/config/ServiceConfig.java | 7 +-
.../dubbo/config/bootstrap/DubboBootstrap.java | 21 +-
.../bootstrap/builders/ApplicationBuilder.java | 28 ++
.../ConfigurableMetadataServiceExporter.java | 72 +++-
.../dubbo/config/AbstractInterfaceConfigTest.java | 3 +
.../dubbo/config/bootstrap/DubboBootstrapTest.java | 4 +-
.../bootstrap/builders/ApplicationBuilderTest.java | 35 +-
.../PublishingServiceDefinitionListenerTest.java | 22 +-
.../dubbo/config/mock/MockServiceDiscovery.java | 69 ++++
.../metadata/MetadataServiceExporterTest.java | 32 +-
...g.apache.dubbo.registry.client.ServiceDiscovery | 1 +
.../spring/registry/MockServiceDiscovery.java | 69 ++++
...g.apache.dubbo.registry.client.ServiceDiscovery | 1 +
dubbo-dependencies-bom/pom.xml | 29 +-
dubbo-distribution/dubbo-all/pom.xml | 16 +
.../metadata/InstanceMetadataChangedListener.java | 27 +-
.../org/apache/dubbo/metadata/MetadataService.java | 42 +++
.../apache/dubbo/qos/command/CommandContext.java | 9 +
.../org/apache/dubbo/qos/command/impl/Live.java | 52 +++
.../org/apache/dubbo/qos/command/impl/Ready.java | 27 +-
.../org/apache/dubbo/qos/command/impl/Startup.java | 52 +++
.../impl/Ready.java => probe/LivenessProbe.java} | 32 +-
.../impl/Ready.java => probe/ReadinessProbe.java} | 32 +-
.../impl/Ready.java => probe/StartupProbe.java} | 32 +-
.../impl/BootstrapReadinessProbe.java} | 17 +-
.../impl/BootstrapStartupProbe.java} | 16 +-
.../qos/probe/impl/ProviderReadinessProbe.java | 52 +++
.../qos/server/handler/HttpProcessHandler.java | 51 ++-
.../org.apache.dubbo.qos.command.BaseCommand | 2 +
.../org.apache.dubbo.qos.probe.ReadinessProbe | 2 +
.../org.apache.dubbo.qos.probe.StartupProbe | 1 +
.../dubbo/qos/command/util/CommandHelperTest.java | 6 +-
.../java/org/apache/dubbo/registry/Constants.java | 10 +
.../client/SelfHostMetaServiceDiscovery.java | 290 +++++++++++++++
.../registry/client/metadata/MetadataUtils.java | 90 +++--
.../StandardMetadataServiceURLBuilder.java | 82 ++++-
.../store/InMemoryWritableMetadataService.java | 23 +-
.../metadata/store/RemoteMetadataServiceImpl.java | 37 +-
.../client/migration/MigrationClusterInvoker.java | 2 +-
.../registry/client/InMemoryServiceDiscovery.java | 9 +-
.../support/ServiceOrientedRegistryTest.java | 5 +
dubbo-registry/dubbo-registry-dns/pom.xml | 90 +++++
.../org/apache/dubbo/registry/dns/DNSRegistry.java | 58 +++
.../dubbo/registry/dns/DNSRegistryFactory.java | 20 +-
.../dubbo/registry/dns/DNSServiceDiscovery.java | 152 ++++++++
.../registry/dns/DNSServiceDiscoveryFactory.java | 18 +-
.../dubbo/registry/dns/util/DNSClientConst.java | 47 +++
.../dubbo/registry/dns/util/DNSResolver.java | 120 ++++++
.../dubbo/registry/dns/util/ResolveResult.java | 66 ++++
.../org.apache.dubbo.registry.RegistryFactory | 1 +
...g.apache.dubbo.registry.client.ServiceDiscovery | 1 +
...e.dubbo.registry.client.ServiceDiscoveryFactory | 1 +
.../registry/dns/DNSServiceDiscoveryTest.java | 191 ++++++++++
.../dubbo/registry/dns/util/DNSResolverTest.java | 23 +-
dubbo-registry/dubbo-registry-kubernetes/pom.xml | 73 ++++
.../registry/kubernetes/KubernetesRegistry.java | 58 +++
.../kubernetes/KubernetesRegistryFactory.java | 20 +-
.../kubernetes/KubernetesServiceDiscovery.java | 404 +++++++++++++++++++++
.../KubernetesServiceDiscoveryFactory.java | 18 +-
.../kubernetes/util/KubernetesClientConst.java | 76 ++++
.../kubernetes/util/KubernetesConfigUtils.java | 111 ++++++
.../org.apache.dubbo.registry.RegistryFactory | 1 +
...g.apache.dubbo.registry.client.ServiceDiscovery | 1 +
...e.dubbo.registry.client.ServiceDiscoveryFactory | 1 +
.../kubernetes/KubernetesServiceDiscoveryTest.java | 191 ++++++++++
.../org.mockito.plugins.MockMaker | 1 +
.../multicast/MulticastServiceDiscovery.java | 72 ++++
.../MulticastServiceDiscoveryFactory.java | 18 +-
...g.apache.dubbo.registry.client.ServiceDiscovery | 1 +
...e.dubbo.registry.client.ServiceDiscoveryFactory | 1 +
.../registry/multicast/MulticastRegistryTest.java | 20 +-
.../zookeeper/ZookeeperServiceDiscoveryTest.java | 4 +-
dubbo-registry/pom.xml | 2 +
.../support/header/HeartbeatHandlerTest.java | 45 ++-
.../transport/netty/ClientReconnectTest.java | 5 +-
.../remoting/transport/netty/ThreadNameTest.java | 4 +-
.../transport/netty4/NettyTransporterTest.java | 4 +-
.../transport/netty4/ReplierDispatcherTest.java | 13 +-
pom.xml | 2 +
83 files changed, 3144 insertions(+), 305 deletions(-)
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvokerTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvokerTest.java
index feff78e..df1240c 100644
--- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvokerTest.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockClusterInvokerTest.java
@@ -38,7 +38,9 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import static org.apache.dubbo.common.constants.CommonConstants.PATH_KEY;
import static org.apache.dubbo.rpc.Constants.MOCK_KEY;
+import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY;
public class MockClusterInvokerTest {
@@ -55,7 +57,8 @@ public class MockClusterInvokerTest {
@Test
public void testMockInvokerInvoke_normal() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName());
- url = url.addParameter(MOCK_KEY, "fail");
+ url = url.addParameter(MOCK_KEY, "fail")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()));
Invoker<IHelloService> cluster = getClusterInvoker(url);
URL mockUrl = URL.valueOf("mock://localhost/" + IHelloService.class.getName()
+ "?getSomething.mock=return aa");
@@ -84,6 +87,7 @@ public class MockClusterInvokerTest {
public void testMockInvokerInvoke_failmock() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
.addParameter(MOCK_KEY, "fail:return null")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("invoke_return_error", "true");
URL mockUrl = URL.valueOf("mock://localhost/" + IHelloService.class.getName()
+ "?getSomething.mock=return aa").addParameters(url.getParameters());
@@ -118,7 +122,8 @@ public class MockClusterInvokerTest {
@Test
public void testMockInvokerInvoke_forcemock() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName());
- url = url.addParameter(MOCK_KEY, "force:return null");
+ url = url.addParameter(MOCK_KEY, "force:return null")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()));
URL mockUrl = URL.valueOf("mock://localhost/" + IHelloService.class.getName()
+ "?getSomething.mock=return aa&getSomething3xx.mock=return xx")
@@ -150,7 +155,9 @@ public class MockClusterInvokerTest {
@Test
public void testMockInvokerInvoke_forcemock_defaultreturn() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName());
- url = url.addParameter(MOCK_KEY, "force");
+ url = url.addParameter(MOCK_KEY, "force")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()));
+
Invoker<IHelloService> cluster = getClusterInvoker(url);
URL mockUrl = URL.valueOf("mock://localhost/" + IHelloService.class.getName()
+ "?getSomething.mock=return aa&getSomething3xx.mock=return xx&sayHello.mock=return ")
@@ -173,7 +180,8 @@ public class MockClusterInvokerTest {
public void testMockInvokerFromOverride_Invoke_Fock_someMethods() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
.addParameter("getSomething.mock", "fail:return x")
- .addParameter("getSomething2.mock", "force:return y");
+ .addParameter("getSomething2.mock", "force:return y")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()));
Invoker<IHelloService> cluster = getClusterInvoker(url);
//Configured with mock
RpcInvocation invocation = new RpcInvocation();
@@ -208,6 +216,7 @@ public class MockClusterInvokerTest {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
.addParameter("getSomething.mock", "fail:return x")
.addParameter("getSomething2.mock", "force:return y")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
//Configured with mock
@@ -242,6 +251,7 @@ public class MockClusterInvokerTest {
.addParameter("mock", "fail:return null")
.addParameter("getSomething.mock", "fail:return x")
.addParameter("getSomething2.mock", "force:return y")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
//Configured with mock
@@ -278,6 +288,7 @@ public class MockClusterInvokerTest {
.addParameter("mock", "fail:return z")
.addParameter("getSomething.mock", "fail:return x")
.addParameter("getSomething2.mock", "force:return y")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
//Configured with mock
@@ -314,6 +325,7 @@ public class MockClusterInvokerTest {
.addParameter("mock", "force:return z")
.addParameter("getSomething.mock", "fail:return x")
.addParameter("getSomething2.mock", "force:return y")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
//Configured with mock
@@ -348,6 +360,7 @@ public class MockClusterInvokerTest {
public void testMockInvokerFromOverride_Invoke_Fock_Default() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
.addParameter("mock", "fail:return x")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
//Configured with mock
@@ -376,6 +389,7 @@ public class MockClusterInvokerTest {
public void testMockInvokerFromOverride_Invoke_checkCompatible_return() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
.addParameter("getSomething.mock", "return x")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
//Configured with mock
@@ -402,6 +416,7 @@ public class MockClusterInvokerTest {
public void testMockInvokerFromOverride_Invoke_checkCompatible_ImplMock() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
.addParameter("mock", "true")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
//Configured with mock
@@ -418,6 +433,7 @@ public class MockClusterInvokerTest {
public void testMockInvokerFromOverride_Invoke_checkCompatible_ImplMock2() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
.addParameter("mock", "fail")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
//Configured with mock
@@ -433,6 +449,7 @@ public class MockClusterInvokerTest {
@Test
public void testMockInvokerFromOverride_Invoke_checkCompatible_ImplMock3() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("mock", "force");
Invoker<IHelloService> cluster = getClusterInvoker(url);
//Configured with mock
@@ -446,6 +463,7 @@ public class MockClusterInvokerTest {
public void testMockInvokerFromOverride_Invoke_check_String() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
.addParameter("getSomething.mock", "force:return 1688")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
//Configured with mock
@@ -459,6 +477,7 @@ public class MockClusterInvokerTest {
@Test
public void testMockInvokerFromOverride_Invoke_check_int() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("getInt1.mock", "force:return 1688")
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
@@ -473,6 +492,7 @@ public class MockClusterInvokerTest {
@Test
public void testMockInvokerFromOverride_Invoke_check_boolean() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("getBoolean1.mock", "force:return true")
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
@@ -487,6 +507,7 @@ public class MockClusterInvokerTest {
@Test
public void testMockInvokerFromOverride_Invoke_check_Boolean() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("getBoolean2.mock", "force:return true")
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
@@ -502,6 +523,7 @@ public class MockClusterInvokerTest {
public void testMockInvokerFromOverride_Invoke_check_ListString_empty() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
.addParameter("getListString.mock", "force:return empty")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
//Configured with mock
@@ -516,6 +538,7 @@ public class MockClusterInvokerTest {
public void testMockInvokerFromOverride_Invoke_check_ListString() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
.addParameter("getListString.mock", "force:return [\"hi\",\"hi2\"]")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
//Configured with mock
@@ -532,6 +555,7 @@ public class MockClusterInvokerTest {
public void testMockInvokerFromOverride_Invoke_check_ListPojo_empty() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
.addParameter("getUsers.mock", "force:return empty")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
//Configured with mock
@@ -546,6 +570,7 @@ public class MockClusterInvokerTest {
public void testMockInvokerFromOverride_Invoke_check_ListPojo() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
.addParameter("getUsers.mock", "force:return [{id:1, name:\"hi1\"}, {id:2, name:\"hi2\"}]")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
//Configured with mock
@@ -562,6 +587,7 @@ public class MockClusterInvokerTest {
public void testMockInvokerFromOverride_Invoke_check_ListPojo_error() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
.addParameter("getUsers.mock", "force:return [{id:x, name:\"hi1\"}]")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
//Configured with mock
@@ -577,6 +603,7 @@ public class MockClusterInvokerTest {
public void testMockInvokerFromOverride_Invoke_force_throw() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
.addParameter("getBoolean2.mock", "force:throw ")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
//Configured with mock
@@ -594,6 +621,7 @@ public class MockClusterInvokerTest {
public void testMockInvokerFromOverride_Invoke_force_throwCustemException() throws Throwable {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
.addParameter("getBoolean2.mock", "force:throw org.apache.dubbo.rpc.cluster.support.wrapper.MyMockException")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
//Configured with mock
@@ -611,6 +639,7 @@ public class MockClusterInvokerTest {
public void testMockInvokerFromOverride_Invoke_force_throwCustemExceptionNotFound() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
.addParameter("getBoolean2.mock", "force:throw java.lang.RuntimeException2")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
//Configured with mock
@@ -628,6 +657,7 @@ public class MockClusterInvokerTest {
public void testMockInvokerFromOverride_Invoke_mock_false() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloService.class.getName())
.addParameter("mock", "false")
+ .addParameter(REFER_KEY, URL.encode(PATH_KEY + "=" + IHelloService.class.getName()))
.addParameter("invoke_return_error", "true");
Invoker<IHelloService> cluster = getClusterInvoker(url);
//Configured with mock
@@ -708,7 +738,7 @@ public class MockClusterInvokerTest {
return "something3";
}
- public String getSomething4(){
+ public String getSomething4() {
throw new RpcException("getSomething4|RpcException");
}
@@ -754,7 +784,7 @@ public class MockClusterInvokerTest {
return "something3mock";
}
- public String getSomething4(){
+ public String getSomething4() {
return "something4mock";
}
diff --git a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockProviderRpcExceptionTest.java b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockProviderRpcExceptionTest.java
index 39459fb..6a46312 100644
--- a/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockProviderRpcExceptionTest.java
+++ b/dubbo-cluster/src/test/java/org/apache/dubbo/rpc/cluster/support/wrapper/MockProviderRpcExceptionTest.java
@@ -37,6 +37,7 @@ import java.util.Arrays;
import java.util.List;
import static org.apache.dubbo.rpc.Constants.MOCK_KEY;
+import static org.apache.dubbo.rpc.cluster.Constants.REFER_KEY;
public class MockProviderRpcExceptionTest {
@@ -53,7 +54,8 @@ public class MockProviderRpcExceptionTest {
@Test
public void testMockInvokerProviderRpcException() {
URL url = URL.valueOf("remote://1.2.3.4/" + IHelloRpcService.class.getName());
- url = url.addParameter(MOCK_KEY, "true").addParameter("invoke_return_error", "true");
+ url = url.addParameter(MOCK_KEY, "true").addParameter("invoke_return_error", "true")
+ .addParameter(REFER_KEY, "path%3dorg.apache.dubbo.rpc.cluster.support.wrapper.MockProviderRpcExceptionTest%24IHelloRpcService");
Invoker<IHelloRpcService> cluster = getClusterInvoker(url);
RpcInvocation invocation = new RpcInvocation();
invocation.setMethodName("getSomething4");
@@ -130,7 +132,7 @@ public class MockProviderRpcExceptionTest {
return "something3";
}
- public String getSomething4(){
+ public String getSomething4() {
throw new RpcException("getSomething4|RpcException");
}
@@ -176,7 +178,7 @@ public class MockProviderRpcExceptionTest {
return "something3mock";
}
- public String getSomething4(){
+ public String getSomething4() {
return "something4mock";
}
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
index 4f831b4..1145f9e 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/common/constants/CommonConstants.java
@@ -363,6 +363,12 @@ public interface CommonConstants {
*/
String DEFAULT_SERVICE_NAME_MAPPING_PROPERTIES_PATH = "META-INF/dubbo/service-name-mapping.properties";
+ String QOS_LIVE_PROBE_EXTENSION = "dubbo.application.liveness-probe";
+
+ String QOS_READY_PROBE_EXTENSION = "dubbo.application.readiness-probe";
+
+ String QOS_STARTUP_PROBE_EXTENSION = "dubbo.application.startup-probe";
+
String REGISTRY_DELAY_NOTIFICATION_KEY = "delay-notification";
String CACHE_CLEAR_TASK_INTERVAL = "dubbo.application.url.cache.task.interval";
diff --git a/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java b/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java
index f77d3f1..3ecef21 100644
--- a/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java
+++ b/dubbo-common/src/main/java/org/apache/dubbo/config/ApplicationConfig.java
@@ -172,6 +172,20 @@ public class ApplicationConfig extends AbstractConfig {
*/
private String protocol;
+ /**
+ * Metadata Service, used in Service Discovery
+ */
+ private Integer metadataServicePort;
+
+ /**
+ * used to set extensions of probe in qos
+ */
+ private String livenessProbe;
+
+ private String readinessProbe;
+
+ private String startupProbe;
+
public ApplicationConfig() {
}
@@ -485,6 +499,42 @@ public class ApplicationConfig extends AbstractConfig {
this.protocol = protocol;
}
+ @Parameter(key = "metadata-service-port")
+ public Integer getMetadataServicePort() {
+ return metadataServicePort;
+ }
+
+ public void setMetadataServicePort(Integer metadataServicePort) {
+ this.metadataServicePort = metadataServicePort;
+ }
+
+ @Parameter(key = "liveness-probe")
+ public String getLivenessProbe() {
+ return livenessProbe;
+ }
+
+ public void setLivenessProbe(String livenessProbe) {
+ this.livenessProbe = livenessProbe;
+ }
+
+ @Parameter(key = "readiness-probe")
+ public String getReadinessProbe() {
+ return readinessProbe;
+ }
+
+ public void setReadinessProbe(String readinessProbe) {
+ this.readinessProbe = readinessProbe;
+ }
+
+ @Parameter(key = "startup-probe")
+ public String getStartupProbe() {
+ return startupProbe;
+ }
+
+ public void setStartupProbe(String startupProbe) {
+ this.startupProbe = startupProbe;
+ }
+
@Override
public void refresh() {
super.refresh();
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
index 3b5edc1..44f7730 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/ServiceConfig.java
@@ -217,7 +217,8 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> {
public void exported() {
List<URL> exportedURLs = this.getExportedUrls();
exportedURLs.forEach(url -> {
- ServiceNameMapping.getExtension(getApplication().getParameters().get(MAPPING_KEY)).map(url);
+ Map<String, String> parameters = getApplication().getParameters();
+ ServiceNameMapping.getExtension(parameters != null ? parameters.get(MAPPING_KEY) : null).map(url);
});
// dispatch a ServiceConfigExportedEvent since 2.7.4
dispatch(new ServiceConfigExportedEvent(this));
@@ -435,7 +436,7 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> {
/**
* Here the token value configured by the provider is used to assign the value to ServiceConfig#token
*/
- if(ConfigUtils.isEmpty(token) && provider != null) {
+ if (ConfigUtils.isEmpty(token) && provider != null) {
token = provider.getToken();
}
@@ -713,7 +714,7 @@ public class ServiceConfig<T> extends ServiceConfigBase<T> {
}
private void postProcessConfig() {
- List<ConfigPostProcessor> configPostProcessors =ExtensionLoader.getExtensionLoader(ConfigPostProcessor.class)
+ List<ConfigPostProcessor> configPostProcessors = ExtensionLoader.getExtensionLoader(ConfigPostProcessor.class)
.getActivateExtension(URL.valueOf("configPostProcessor://"), (String[]) null);
configPostProcessors.forEach(component -> component.postProcessServiceConfig(this));
}
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
index 48bc281..a4116f4 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/DubboBootstrap.java
@@ -170,10 +170,12 @@ public class DubboBootstrap extends GenericEventListener {
private AtomicBoolean started = new AtomicBoolean(false);
- private AtomicBoolean ready = new AtomicBoolean(true);
+ private AtomicBoolean startup = new AtomicBoolean(true);
private AtomicBoolean destroyed = new AtomicBoolean(false);
+ private AtomicBoolean shutdown = new AtomicBoolean(false);
+
private volatile ServiceInstance serviceInstance;
private volatile MetadataService metadataService;
@@ -878,7 +880,7 @@ public class DubboBootstrap extends GenericEventListener {
*/
public DubboBootstrap start() {
if (started.compareAndSet(false, true)) {
- ready.set(false);
+ startup.set(false);
initialize();
if (logger.isInfoEnabled()) {
logger.info(NAME + " is starting...");
@@ -902,13 +904,13 @@ public class DubboBootstrap extends GenericEventListener {
} catch (Exception e) {
logger.warn(NAME + " exportAsync occurred an exception.");
}
- ready.set(true);
+ startup.set(true);
if (logger.isInfoEnabled()) {
logger.info(NAME + " is ready.");
}
}).start();
} else {
- ready.set(true);
+ startup.set(true);
if (logger.isInfoEnabled()) {
logger.info(NAME + " is ready.");
}
@@ -973,8 +975,12 @@ public class DubboBootstrap extends GenericEventListener {
return started.get();
}
- public boolean isReady() {
- return ready.get();
+ public boolean isStartup() {
+ return startup.get();
+ }
+
+ public boolean isShutdown() {
+ return shutdown.get();
}
public DubboBootstrap stop() throws IllegalStateException {
@@ -1197,7 +1203,8 @@ public class DubboBootstrap extends GenericEventListener {
}
public void destroy() {
- if (destroyLock.tryLock()) {
+ if (destroyLock.tryLock()
+ && shutdown.compareAndSet(false, true)) {
try {
DubboShutdownHook.destroyAll();
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ApplicationBuilder.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ApplicationBuilder.java
index 33e6f54..9c17afa 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ApplicationBuilder.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/bootstrap/builders/ApplicationBuilder.java
@@ -51,6 +51,10 @@ public class ApplicationBuilder extends AbstractBuilder<ApplicationConfig, Appli
private Boolean qosAcceptForeignIp;
private Map<String, String> parameters;
private String shutwait;
+ private Integer metadataServicePort;
+ private String livenessProbe;
+ private String readinessProbe;
+ private String startupProbe;
public static ApplicationBuilder newBuilder() {
return new ApplicationBuilder();
@@ -172,6 +176,26 @@ public class ApplicationBuilder extends AbstractBuilder<ApplicationConfig, Appli
return getThis();
}
+ public ApplicationBuilder metadataServicePort(Integer metadataServicePort) {
+ this.metadataServicePort = metadataServicePort;
+ return getThis();
+ }
+
+ public ApplicationBuilder livenessProbe(String livenessProbe) {
+ this.livenessProbe = livenessProbe;
+ return getThis();
+ }
+
+ public ApplicationBuilder readinessProbe(String readinessProbe) {
+ this.readinessProbe = readinessProbe;
+ return getThis();
+ }
+
+ public ApplicationBuilder startupProbe(String startupProbe) {
+ this.startupProbe = startupProbe;
+ return getThis();
+ }
+
public ApplicationConfig build() {
ApplicationConfig config = new ApplicationConfig();
super.build(config);
@@ -193,6 +217,10 @@ public class ApplicationBuilder extends AbstractBuilder<ApplicationConfig, Appli
config.setQosEnable(this.qosEnable);
config.setQosPort(this.qosPort);
config.setQosAcceptForeignIp(this.qosAcceptForeignIp);
+ config.setMetadataServicePort(this.metadataServicePort);
+ config.setLivenessProbe(this.livenessProbe);
+ config.setReadinessProbe(this.readinessProbe);
+ config.setStartupProbe(this.startupProbe);
config.setParameters(this.parameters);
if (!StringUtils.isEmpty(shutwait)) {
config.setShutwait(shutwait);
diff --git a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
index edb2d0c..faae7d7 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/org/apache/dubbo/config/metadata/ConfigurableMetadataServiceExporter.java
@@ -19,7 +19,10 @@ package org.apache.dubbo.config.metadata;
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.CollectionUtils;
import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.config.ArgumentConfig;
+import org.apache.dubbo.config.MethodConfig;
import org.apache.dubbo.config.ProtocolConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.ServiceConfig;
@@ -28,10 +31,12 @@ import org.apache.dubbo.metadata.MetadataService;
import org.apache.dubbo.metadata.MetadataServiceExporter;
import org.apache.dubbo.rpc.model.ApplicationModel;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import static java.util.Collections.emptyList;
-import static org.apache.dubbo.common.constants.CommonConstants.DUBBO;
+import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL;
/**
* {@link MetadataServiceExporter} implementation based on {@link ConfigManager Dubbo configurations}, the clients
@@ -73,6 +78,7 @@ public class ConfigurableMetadataServiceExporter implements MetadataServiceExpor
serviceConfig.setRef(metadataService);
serviceConfig.setGroup(getApplicationConfig().getName());
serviceConfig.setVersion(metadataService.version());
+ serviceConfig.setMethods(generateMethodConfig());
// export
serviceConfig.export();
@@ -92,6 +98,28 @@ public class ConfigurableMetadataServiceExporter implements MetadataServiceExpor
return this;
}
+ /**
+ * Generate Method Config for Service Discovery Metadata <p/>
+ * <p>
+ * Make {@link MetadataService} support argument callback,
+ * used to notify {@link org.apache.dubbo.registry.client.ServiceInstance}'s
+ * metadata change event
+ *
+ * @since 3.0
+ */
+ private List<MethodConfig> generateMethodConfig() {
+ MethodConfig methodConfig = new MethodConfig();
+ methodConfig.setName("getAndListenInstanceMetadata");
+
+ ArgumentConfig argumentConfig = new ArgumentConfig();
+ argumentConfig.setIndex(1);
+ argumentConfig.setCallback(true);
+
+ methodConfig.setArguments(Collections.singletonList(argumentConfig));
+
+ return Collections.singletonList(methodConfig);
+ }
+
@Override
public ConfigurableMetadataServiceExporter unexport() {
if (isExported()) {
@@ -115,10 +143,44 @@ public class ConfigurableMetadataServiceExporter implements MetadataServiceExpor
private ProtocolConfig generateMetadataProtocol() {
ProtocolConfig defaultProtocol = new ProtocolConfig();
- defaultProtocol.setName(DUBBO);
- // defaultProtocol.setHost() ?
- // auto-increment port
- defaultProtocol.setPort(-1);
+ Integer port = getApplicationConfig().getMetadataServicePort();
+
+ if (port == null || port < -1) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Metadata Service Port hasn't been set will use default protocol defined in protocols.");
+ }
+ List<ProtocolConfig> defaultProtocols = ApplicationModel.getConfigManager().getDefaultProtocols();
+
+ ProtocolConfig dubboProtocol = findDubboProtocol(defaultProtocols);
+ if (dubboProtocol != null) {
+ logger.info("Using dubbo protocol " + dubboProtocol + " to export MetadataService.");
+ return dubboProtocol;
+ } else {
+ defaultProtocol.setName(DUBBO_PROTOCOL);
+ defaultProtocol.setPort(-1);
+ }
+
+ } else {
+ defaultProtocol.setName(DUBBO_PROTOCOL);
+ defaultProtocol.setPort(port);
+ }
+
+ logger.info("Using dubbo protocol " + defaultProtocol + " to export MetadataService.");
+
return defaultProtocol;
}
+
+ private ProtocolConfig findDubboProtocol(List<ProtocolConfig> protocolConfigs) {
+ if (CollectionUtils.isEmpty(protocolConfigs)) {
+ return null;
+ }
+
+ for (ProtocolConfig protocolConfig : protocolConfigs) {
+ if (DUBBO_PROTOCOL.equalsIgnoreCase(protocolConfig.getName())) {
+ return protocolConfig;
+ }
+ }
+
+ return null;
+ }
}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractInterfaceConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractInterfaceConfigTest.java
index 2bcd960..3a4a83c 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractInterfaceConfigTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/AbstractInterfaceConfigTest.java
@@ -24,6 +24,7 @@ import org.apache.dubbo.config.mock.GreetingLocal3;
import org.apache.dubbo.config.mock.GreetingMock1;
import org.apache.dubbo.config.mock.GreetingMock2;
import org.apache.dubbo.config.utils.ConfigValidationUtils;
+import org.apache.dubbo.rpc.model.ApplicationModel;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
@@ -41,12 +42,14 @@ public class AbstractInterfaceConfigTest {
@BeforeAll
public static void setUp(@TempDir Path folder) {
+ ApplicationModel.reset();
dubboProperties = folder.resolve(CommonConstants.DUBBO_PROPERTIES_KEY).toFile();
System.setProperty(CommonConstants.DUBBO_PROPERTIES_KEY, dubboProperties.getAbsolutePath());
}
@AfterAll
public static void tearDown() {
+ ApplicationModel.reset();
System.clearProperty(CommonConstants.DUBBO_PROPERTIES_KEY);
}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/DubboBootstrapTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/DubboBootstrapTest.java
index 6136dd4..e329f23 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/DubboBootstrapTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/DubboBootstrapTest.java
@@ -25,6 +25,7 @@ import org.apache.dubbo.config.MonitorConfig;
import org.apache.dubbo.config.utils.ConfigValidationUtils;
import org.apache.dubbo.monitor.MonitorService;
import org.apache.dubbo.registry.RegistryService;
+import org.apache.dubbo.rpc.model.ApplicationModel;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
@@ -55,13 +56,14 @@ public class DubboBootstrapTest {
@BeforeAll
public static void setUp(@TempDir Path folder) {
+ ApplicationModel.reset();
dubboProperties = folder.resolve(CommonConstants.DUBBO_PROPERTIES_KEY).toFile();
System.setProperty(CommonConstants.DUBBO_PROPERTIES_KEY, dubboProperties.getAbsolutePath());
}
@AfterEach
public void tearDown() throws IOException {
-
+ ApplicationModel.reset();
}
@Test
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/ApplicationBuilderTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/ApplicationBuilderTest.java
index 6c37eae..0ced7cf 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/ApplicationBuilderTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/bootstrap/builders/ApplicationBuilderTest.java
@@ -214,6 +214,34 @@ class ApplicationBuilderTest {
}
@Test
+ void metadataServicePort() {
+ ApplicationBuilder builder = new ApplicationBuilder();
+ builder.metadataServicePort(12345);
+ Assertions.assertEquals(12345, builder.build().getMetadataServicePort());
+ }
+
+ @Test
+ void livenessProbe() {
+ ApplicationBuilder builder = new ApplicationBuilder();
+ builder.livenessProbe("TestProbe");
+ Assertions.assertEquals("TestProbe", builder.build().getLivenessProbe());
+ }
+
+ @Test
+ void readinessProbe() {
+ ApplicationBuilder builder = new ApplicationBuilder();
+ builder.readinessProbe("TestProbe");
+ Assertions.assertEquals("TestProbe", builder.build().getReadinessProbe());
+ }
+
+ @Test
+ void startupProbe() {
+ ApplicationBuilder builder = new ApplicationBuilder();
+ builder.startupProbe("TestProbe");
+ Assertions.assertEquals("TestProbe", builder.build().getStartupProbe());
+ }
+
+ @Test
void build() {
MonitorConfig monitor = new MonitorConfig("monitor-addr");
RegistryConfig registry = new RegistryConfig();
@@ -223,7 +251,8 @@ class ApplicationBuilderTest {
.environment("develop").compiler("compiler").logger("log4j").monitor(monitor).isDefault(false)
.dumpDirectory("dumpDirectory").qosEnable(true).qosPort(8080).qosAcceptForeignIp(false)
.shutwait("shutwait").registryIds("registryIds").addRegistry(registry)
- .appendParameter("default.num", "one");
+ .appendParameter("default.num", "one").metadataServicePort(12345)
+ .livenessProbe("liveness").readinessProbe("readiness").startupProbe("startup");
ApplicationConfig config = builder.build();
ApplicationConfig config2 = builder.build();
@@ -249,6 +278,10 @@ class ApplicationBuilderTest {
Assertions.assertSame(registry, config.getRegistry());
Assertions.assertTrue(config.getParameters().containsKey("default.num"));
Assertions.assertEquals("one", config.getParameters().get("default.num"));
+ Assertions.assertEquals(12345, config.getMetadataServicePort());
+ Assertions.assertEquals("liveness", config.getLivenessProbe());
+ Assertions.assertEquals("readiness", config.getReadinessProbe());
+ Assertions.assertEquals("startup", config.getStartupProbe());
Assertions.assertNotSame(config, config2);
}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/listener/PublishingServiceDefinitionListenerTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/listener/PublishingServiceDefinitionListenerTest.java
index e7e5376..3190f34 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/listener/PublishingServiceDefinitionListenerTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/event/listener/PublishingServiceDefinitionListenerTest.java
@@ -16,7 +16,6 @@
*/
package org.apache.dubbo.config.event.listener;
-import org.apache.dubbo.common.URL;
import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.config.RegistryConfig;
import org.apache.dubbo.config.ServiceConfig;
@@ -25,7 +24,8 @@ import org.apache.dubbo.config.bootstrap.EchoServiceImpl;
import org.apache.dubbo.config.context.ConfigManager;
import org.apache.dubbo.config.event.ServiceConfigExportedEvent;
import org.apache.dubbo.metadata.WritableMetadataService;
-import org.apache.dubbo.metadata.definition.model.FullServiceDefinition;
+import org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder;
+import org.apache.dubbo.metadata.definition.model.ServiceDefinition;
import org.apache.dubbo.rpc.model.ApplicationModel;
import com.google.gson.Gson;
@@ -33,14 +33,7 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import java.util.List;
-
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE;
-import static org.apache.dubbo.common.constants.CommonConstants.PID_KEY;
-import static org.apache.dubbo.common.constants.CommonConstants.TIMESTAMP_KEY;
-import static org.apache.dubbo.metadata.definition.ServiceDefinitionBuilder.buildFullDefinition;
-import static org.apache.dubbo.remoting.Constants.BIND_IP_KEY;
-import static org.apache.dubbo.remoting.Constants.BIND_PORT_KEY;
import static org.junit.jupiter.api.Assertions.assertEquals;
/**
@@ -80,15 +73,8 @@ public class PublishingServiceDefinitionListenerTest {
String serviceDefinition = writableMetadataService.getServiceDefinition(EchoService.class.getName());
- List<URL> exportedUrls = serviceConfig.getExportedUrls();
-
- FullServiceDefinition fullServiceDefinition = buildFullDefinition(
- serviceConfig.getInterfaceClass(),
- exportedUrls.get(0)
- .removeParameters(PID_KEY, TIMESTAMP_KEY, BIND_IP_KEY, BIND_PORT_KEY, TIMESTAMP_KEY)
- .getParameters()
- );
+ ServiceDefinition serviceDefinitionBuild = ServiceDefinitionBuilder.build(serviceConfig.getInterfaceClass());
- assertEquals(serviceDefinition, new Gson().toJson(fullServiceDefinition));
+ assertEquals(serviceDefinition, new Gson().toJson(serviceDefinitionBuild));
}
}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockServiceDiscovery.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockServiceDiscovery.java
new file mode 100644
index 0000000..33f1a16
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/config/mock/MockServiceDiscovery.java
@@ -0,0 +1,69 @@
+/*
+ * 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.mock;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.client.ServiceDiscovery;
+import org.apache.dubbo.registry.client.ServiceInstance;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class MockServiceDiscovery implements ServiceDiscovery {
+ private URL registryURL;
+ private ServiceInstance serviceInstance;
+
+ @Override
+ public void initialize(URL registryURL) throws Exception {
+ this.registryURL = registryURL;
+ }
+
+ @Override
+ public void destroy() throws Exception {
+
+ }
+
+ @Override
+ public void register(ServiceInstance serviceInstance) throws RuntimeException {
+ this.serviceInstance = serviceInstance;
+ }
+
+ @Override
+ public void update(ServiceInstance serviceInstance) throws RuntimeException {
+ this.serviceInstance = serviceInstance;
+ }
+
+ @Override
+ public void unregister(ServiceInstance serviceInstance) throws RuntimeException {
+ this.serviceInstance = null;
+ }
+
+ @Override
+ public Set<String> getServices() {
+ return new HashSet<>();
+ }
+
+ @Override
+ public URL getUrl() {
+ return registryURL;
+ }
+
+ @Override
+ public ServiceInstance getLocalInstance() {
+ return serviceInstance;
+ }
+}
diff --git a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/metadata/MetadataServiceExporterTest.java b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/metadata/MetadataServiceExporterTest.java
index 8096276..aaabe02 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/metadata/MetadataServiceExporterTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/org/apache/dubbo/metadata/MetadataServiceExporterTest.java
@@ -16,12 +16,20 @@
*/
package org.apache.dubbo.metadata;
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.config.ProtocolConfig;
+import org.apache.dubbo.config.RegistryConfig;
+import org.apache.dubbo.config.metadata.ConfigurableMetadataServiceExporter;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
import static org.apache.dubbo.common.constants.CommonConstants.COMPOSITE_METADATA_STORAGE_TYPE;
import static org.apache.dubbo.common.constants.CommonConstants.DEFAULT_METADATA_STORAGE_TYPE;
import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
-import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
@@ -31,10 +39,28 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
*/
public class MetadataServiceExporterTest {
+ @BeforeAll
+ public static void init() {
+ ApplicationModel.getConfigManager().setApplication(new ApplicationConfig("Test"));
+ ApplicationModel.getConfigManager().addRegistry(new RegistryConfig("multicast://224.5.6.7:1234"));
+ ApplicationModel.getConfigManager().addProtocol(new ProtocolConfig("injvm"));
+ }
+
+ @AfterAll
+ public static void destroy() {
+ ApplicationModel.getConfigManager().setApplication(null);
+ ApplicationModel.reset();
+ }
+
@Test
public void test() {
- MetadataServiceExporter exporter = MetadataServiceExporter.getExtension(null);
- assertEquals(exporter, MetadataServiceExporter.getDefaultExtension());
+ MetadataService metadataService = Mockito.mock(MetadataService.class);
+ MetadataServiceExporter exporter = new ConfigurableMetadataServiceExporter(metadataService);
+
+ exporter.export();
+ assertTrue(exporter.isExported());
+ exporter.unexport();
+
assertTrue(exporter.supports(DEFAULT_METADATA_STORAGE_TYPE));
assertTrue(exporter.supports(REMOTE_METADATA_STORAGE_TYPE));
assertTrue(exporter.supports(COMPOSITE_METADATA_STORAGE_TYPE));
diff --git a/dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.registry.client.ServiceDiscovery b/dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.registry.client.ServiceDiscovery
new file mode 100644
index 0000000..22ceb16
--- /dev/null
+++ b/dubbo-config/dubbo-config-api/src/test/resources/META-INF/services/org.apache.dubbo.registry.client.ServiceDiscovery
@@ -0,0 +1 @@
+mockregistry=org.apache.dubbo.config.mock.MockServiceDiscovery
\ No newline at end of file
diff --git a/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/MockServiceDiscovery.java b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/MockServiceDiscovery.java
new file mode 100644
index 0000000..1511b0e
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/java/org/apache/dubbo/config/spring/registry/MockServiceDiscovery.java
@@ -0,0 +1,69 @@
+/*
+ * 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.registry;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.client.ServiceDiscovery;
+import org.apache.dubbo.registry.client.ServiceInstance;
+
+import java.util.HashSet;
+import java.util.Set;
+
+public class MockServiceDiscovery implements ServiceDiscovery {
+ private URL registryURL;
+ private ServiceInstance serviceInstance;
+
+ @Override
+ public void initialize(URL registryURL) throws Exception {
+ this.registryURL = registryURL;
+ }
+
+ @Override
+ public void destroy() throws Exception {
+
+ }
+
+ @Override
+ public void register(ServiceInstance serviceInstance) throws RuntimeException {
+ this.serviceInstance = serviceInstance;
+ }
+
+ @Override
+ public void update(ServiceInstance serviceInstance) throws RuntimeException {
+ this.serviceInstance = serviceInstance;
+ }
+
+ @Override
+ public void unregister(ServiceInstance serviceInstance) throws RuntimeException {
+ this.serviceInstance = null;
+ }
+
+ @Override
+ public Set<String> getServices() {
+ return new HashSet<>();
+ }
+
+ @Override
+ public URL getUrl() {
+ return registryURL;
+ }
+
+ @Override
+ public ServiceInstance getLocalInstance() {
+ return serviceInstance;
+ }
+}
diff --git a/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
new file mode 100644
index 0000000..cea6b84
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/test/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
@@ -0,0 +1 @@
+mock=org.apache.dubbo.config.spring.registry.MockServiceDiscovery
\ No newline at end of file
diff --git a/dubbo-dependencies-bom/pom.xml b/dubbo-dependencies-bom/pom.xml
index 634586d..8d84b38 100644
--- a/dubbo-dependencies-bom/pom.xml
+++ b/dubbo-dependencies-bom/pom.xml
@@ -92,9 +92,9 @@
<spring_version>4.3.16.RELEASE</spring_version>
<javassist_version>3.20.0-GA</javassist_version>
<netty_version>3.2.5.Final</netty_version>
- <netty4_version>4.1.25.Final</netty4_version>
+ <netty4_version>4.1.51.Final</netty4_version>
<mina_version>1.1.7</mina_version>
- <grizzly_version>2.1.4</grizzly_version>
+ <grizzly_version>2.4.4</grizzly_version>
<httpclient_version>4.5.3</httpclient_version>
<httpcore_version>4.4.6</httpcore_version>
<fastjson_version>1.2.70</fastjson_version>
@@ -129,9 +129,9 @@
<rs_api_version>2.0</rs_api_version>
<resteasy_version>3.0.19.Final</resteasy_version>
<tomcat_embed_version>8.5.31</tomcat_embed_version>
- <jetcd_version>0.4.1</jetcd_version>
+ <jetcd_version>0.5.3</jetcd_version>
<nacos_version>1.3.1</nacos_version>
- <grpc.version>1.22.1</grpc.version>
+ <grpc.version>1.31.1</grpc.version>
<!-- Log libs -->
<slf4j_version>1.7.25</slf4j_version>
<jcl_version>1.2</jcl_version>
@@ -145,13 +145,16 @@
<!-- Eureka -->
<eureka.version>1.9.12</eureka.version>
+ <!-- Fabric8 for Kubernetes -->
+ <fabric8_kubernetes_version>4.10.3</fabric8_kubernetes_version>
+
<!-- Alibaba -->
<alibaba_spring_context_support_version>1.0.8</alibaba_spring_context_support_version>
<jaxb_version>2.2.7</jaxb_version>
<activation_version>1.2.0</activation_version>
<test_container_version>1.11.2</test_container_version>
- <etcd_launcher_version>0.3.0</etcd_launcher_version>
+ <etcd_launcher_version>0.5.3</etcd_launcher_version>
<hessian_lite_version>3.2.8</hessian_lite_version>
<swagger_version>1.5.19</swagger_version>
<spring_test_version>4.3.16.RELEASE</spring_test_version>
@@ -468,11 +471,6 @@
</exclusion>
</exclusions>
</dependency>
- <dependency>
- <groupId>io.etcd</groupId>
- <artifactId>jetcd-launcher</artifactId>
- <version>${jetcd_version}</version>
- </dependency>
<!-- Log libs -->
<dependency>
<groupId>org.slf4j</groupId>
@@ -703,6 +701,17 @@
<artifactId>grpc-grpclb</artifactId>
<version>${grpc.version}</version>
</dependency>
+ <dependency>
+ <groupId>io.fabric8</groupId>
+ <artifactId>kubernetes-client</artifactId>
+ <version>${fabric8_kubernetes_version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.fabric8</groupId>
+ <artifactId>kubernetes-server-mock</artifactId>
+ <scope>test</scope>
+ <version>${fabric8_kubernetes_version}</version>
+ </dependency>
</dependencies>
</dependencyManagement>
diff --git a/dubbo-distribution/dubbo-all/pom.xml b/dubbo-distribution/dubbo-all/pom.xml
index 5f67930..e8e4406 100644
--- a/dubbo-distribution/dubbo-all/pom.xml
+++ b/dubbo-distribution/dubbo-all/pom.xml
@@ -159,6 +159,20 @@
</dependency>
<dependency>
<groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-registry-kubernetes</artifactId>
+ <version>${project.version}</version>
+ <scope>compile</scope>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-registry-dns</artifactId>
+ <version>${project.version}</version>
+ <scope>compile</scope>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
<artifactId>dubbo-monitor-api</artifactId>
<version>${project.version}</version>
<scope>compile</scope>
@@ -358,6 +372,8 @@
<include>org.apache.dubbo:dubbo-registry-zookeeper</include>
<include>org.apache.dubbo:dubbo-registry-nacos</include>
<include>org.apache.dubbo:dubbo-registry-multiple</include>
+ <include>org.apache.dubbo:dubbo-registry-kubernetes</include>
+ <include>org.apache.dubbo:dubbo-registry-dns</include>
<include>org.apache.dubbo:dubbo-monitor-api</include>
<include>org.apache.dubbo:dubbo-monitor-default</include>
<include>org.apache.dubbo:dubbo-container-api</include>
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/InstanceMetadataChangedListener.java
similarity index 60%
copy from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
copy to dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/InstanceMetadataChangedListener.java
index 06457f6..5536e53 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/InstanceMetadataChangedListener.java
@@ -14,19 +14,22 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.qos.command.impl;
+package org.apache.dubbo.metadata;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.qos.command.BaseCommand;
-import org.apache.dubbo.qos.command.CommandContext;
-import org.apache.dubbo.qos.command.annotation.Cmd;
+public interface InstanceMetadataChangedListener {
+ /**
+ * Call when metadata in provider side update <p/>
+ * Used to notify consumer to update metadata of ServiceInstance
+ *
+ * @param metadata latest metadata
+ */
+ void onEvent(String metadata);
-@Cmd(name = "start",summary = "Judge if service has started? ")
-public class Ready implements BaseCommand {
-
- @Override
- public String execute(CommandContext commandContext, String[] args) {
- return DubboBootstrap.getInstance().isReady() ? "true" : "false";
+ /**
+ * Echo test
+ * Used to check consumer still online
+ */
+ default String echo(String msg) {
+ return msg;
}
-
}
diff --git a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataService.java b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataService.java
index 24dc82d..c945d73 100644
--- a/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataService.java
+++ b/dubbo-metadata/dubbo-metadata-api/src/main/java/org/apache/dubbo/metadata/MetadataService.java
@@ -233,4 +233,46 @@ public interface MetadataService {
static SortedSet<String> toSortedStrings(Stream<URL> stream) {
return unmodifiableSortedSet(stream.map(URL::toFullString).collect(TreeSet::new, Set::add, Set::addAll));
}
+
+ /**
+ * Export Metadata in Service Instance of Service Discovery
+ * <p>
+ * Used for consumer to get Service Instance Metadata
+ * if Registry is unsupported with publishing metadata
+ *
+ * @param instanceMetadata {@link Map} of provider Service Instance Metadata
+ * @since 3.0
+ */
+ default void exportInstanceMetadata(String instanceMetadata) {
+ throw new UnsupportedOperationException("This operation is not supported for consumer.");
+ }
+
+ /**
+ * Get all Metadata listener from local
+ * <p>
+ * Used for consumer to get Service Instance Metadata
+ * if Registry is unsupported with publishing metadata
+ *
+ * @return {@link Map} of {@link InstanceMetadataChangedListener}
+ * @since 3.0
+ */
+ default Map<String, InstanceMetadataChangedListener> getInstanceMetadataChangedListenerMap() {
+ throw new UnsupportedOperationException("This operation is not supported for consumer.");
+ }
+
+ /**
+ * 1. Fetch Metadata in Service Instance of Service Discovery
+ * 2. Add a metadata change listener
+ * <p>
+ * Used for consumer to get Service Instance Metadata
+ * if Registry is unsupported with publishing metadata
+ *
+ * @param consumerId consumerId
+ * @param listener {@link InstanceMetadataChangedListener} used to notify event
+ * @return {@link Map} of provider Service Instance Metadata
+ * @since 3.0
+ */
+ default String getAndListenInstanceMetadata(String consumerId, InstanceMetadataChangedListener listener) {
+ throw new UnsupportedOperationException("This operation is not supported for consumer.");
+ }
}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/CommandContext.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/CommandContext.java
index 0bd427b..0c2612a 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/CommandContext.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/CommandContext.java
@@ -25,6 +25,7 @@ public class CommandContext {
private Channel remote;
private boolean isHttp;
private Object originRequest;
+ private int httpCode = 200;
public CommandContext(String commandName) {
this.commandName = commandName;
@@ -75,4 +76,12 @@ public class CommandContext {
public void setOriginRequest(Object originRequest) {
this.originRequest = originRequest;
}
+
+ public int getHttpCode() {
+ return httpCode;
+ }
+
+ public void setHttpCode(int httpCode) {
+ this.httpCode = httpCode;
+ }
}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Live.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Live.java
new file mode 100644
index 0000000..d355845
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Live.java
@@ -0,0 +1,52 @@
+/*
+ * 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.qos.command.impl;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.command.annotation.Cmd;
+import org.apache.dubbo.qos.probe.LivenessProbe;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import java.util.List;
+
+@Cmd(name = "live", summary = "Judge if service is alive? ")
+public class Live implements BaseCommand {
+
+ @Override
+ public String execute(CommandContext commandContext, String[] args) {
+ URL url = URL.valueOf("application://")
+ .addParameter(CommonConstants.QOS_LIVE_PROBE_EXTENSION, ApplicationModel.getApplicationConfig().getLivenessProbe());
+ List<LivenessProbe> livenessProbes = ExtensionLoader.getExtensionLoader(LivenessProbe.class)
+ .getActivateExtension(url, CommonConstants.QOS_LIVE_PROBE_EXTENSION);
+ if (!livenessProbes.isEmpty()) {
+ for (LivenessProbe livenessProbe : livenessProbes) {
+ if (!livenessProbe.check()) {
+ // 503 Service Unavailable
+ commandContext.setHttpCode(503);
+ return "false";
+ }
+ }
+ }
+ // 200 OK
+ commandContext.setHttpCode(200);
+ return "true";
+ }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
index 06457f6..109adbc 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
@@ -16,17 +16,38 @@
*/
package org.apache.dubbo.qos.command.impl;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.extension.ExtensionLoader;
import org.apache.dubbo.qos.command.BaseCommand;
import org.apache.dubbo.qos.command.CommandContext;
import org.apache.dubbo.qos.command.annotation.Cmd;
+import org.apache.dubbo.qos.probe.ReadinessProbe;
+import org.apache.dubbo.rpc.model.ApplicationModel;
-@Cmd(name = "start",summary = "Judge if service has started? ")
+import java.util.List;
+
+@Cmd(name = "ready", summary = "Judge if service is ready to work? ")
public class Ready implements BaseCommand {
@Override
public String execute(CommandContext commandContext, String[] args) {
- return DubboBootstrap.getInstance().isReady() ? "true" : "false";
+ URL url = URL.valueOf("application://")
+ .addParameter(CommonConstants.QOS_READY_PROBE_EXTENSION, ApplicationModel.getApplicationConfig().getReadinessProbe());
+ List<ReadinessProbe> readinessProbes = ExtensionLoader.getExtensionLoader(ReadinessProbe.class)
+ .getActivateExtension(url, CommonConstants.QOS_READY_PROBE_EXTENSION);
+ if (!readinessProbes.isEmpty()) {
+ for (ReadinessProbe readinessProbe : readinessProbes) {
+ if (!readinessProbe.check()) {
+ // 503 Service Unavailable
+ commandContext.setHttpCode(503);
+ return "false";
+ }
+ }
+ }
+ // 200 OK
+ commandContext.setHttpCode(200);
+ return "true";
}
}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Startup.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Startup.java
new file mode 100644
index 0000000..e9a5b54
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Startup.java
@@ -0,0 +1,52 @@
+/*
+ * 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.qos.command.impl;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.extension.ExtensionLoader;
+import org.apache.dubbo.qos.command.BaseCommand;
+import org.apache.dubbo.qos.command.CommandContext;
+import org.apache.dubbo.qos.command.annotation.Cmd;
+import org.apache.dubbo.qos.probe.StartupProbe;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import java.util.List;
+
+@Cmd(name = "startup", summary = "Judge if service has started? ")
+public class Startup implements BaseCommand {
+
+ @Override
+ public String execute(CommandContext commandContext, String[] args) {
+ URL url = URL.valueOf("application://")
+ .addParameter(CommonConstants.QOS_STARTUP_PROBE_EXTENSION, ApplicationModel.getApplicationConfig().getStartupProbe());
+ List<StartupProbe> startupProbes = ExtensionLoader.getExtensionLoader(StartupProbe.class)
+ .getActivateExtension(url, CommonConstants.QOS_STARTUP_PROBE_EXTENSION);
+ if (!startupProbes.isEmpty()) {
+ for (StartupProbe startupProbe : startupProbes) {
+ if (!startupProbe.check()) {
+ // 503 Service Unavailable
+ commandContext.setHttpCode(503);
+ return "false";
+ }
+ }
+ }
+ // 200 OK
+ commandContext.setHttpCode(200);
+ return "true";
+ }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/LivenessProbe.java
similarity index 60%
copy from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
copy to dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/LivenessProbe.java
index 06457f6..15484d3 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/LivenessProbe.java
@@ -14,19 +14,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.qos.command.impl;
+package org.apache.dubbo.qos.probe;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.qos.command.BaseCommand;
-import org.apache.dubbo.qos.command.CommandContext;
-import org.apache.dubbo.qos.command.annotation.Cmd;
-
-@Cmd(name = "start",summary = "Judge if service has started? ")
-public class Ready implements BaseCommand {
-
- @Override
- public String execute(CommandContext commandContext, String[] args) {
- return DubboBootstrap.getInstance().isReady() ? "true" : "false";
- }
+import org.apache.dubbo.common.extension.SPI;
+/**
+ * A probe to indicate whether program is alive
+ * </p>
+ * If one or more spi return false, 'live' command in dubbo-qos
+ * will return false. This can be extend with custom program and developers
+ * can implement this to customize life cycle.
+ *
+ * @since 3.0
+ */
+@SPI
+public interface LivenessProbe {
+ /**
+ * Check if program is alive
+ *
+ * @return {@link boolean} result of probe
+ */
+ boolean check();
}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/ReadinessProbe.java
similarity index 60%
copy from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
copy to dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/ReadinessProbe.java
index 06457f6..d988f38 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/ReadinessProbe.java
@@ -14,19 +14,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.qos.command.impl;
+package org.apache.dubbo.qos.probe;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.qos.command.BaseCommand;
-import org.apache.dubbo.qos.command.CommandContext;
-import org.apache.dubbo.qos.command.annotation.Cmd;
-
-@Cmd(name = "start",summary = "Judge if service has started? ")
-public class Ready implements BaseCommand {
-
- @Override
- public String execute(CommandContext commandContext, String[] args) {
- return DubboBootstrap.getInstance().isReady() ? "true" : "false";
- }
+import org.apache.dubbo.common.extension.SPI;
+/**
+ * A probe to indicate whether program is ready
+ * </p>
+ * If one or more spi return false, 'ready' command in dubbo-qos
+ * will return false. This can be extend with custom program and developers
+ * can implement this to customize life cycle.
+ *
+ * @since 3.0
+ */
+@SPI
+public interface ReadinessProbe {
+ /**
+ * Check if program is Ready
+ *
+ * @return {@link boolean} result of probe
+ */
+ boolean check();
}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/StartupProbe.java
similarity index 60%
copy from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
copy to dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/StartupProbe.java
index 06457f6..45bdd4b 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/StartupProbe.java
@@ -14,19 +14,25 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.qos.command.impl;
+package org.apache.dubbo.qos.probe;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.qos.command.BaseCommand;
-import org.apache.dubbo.qos.command.CommandContext;
-import org.apache.dubbo.qos.command.annotation.Cmd;
-
-@Cmd(name = "start",summary = "Judge if service has started? ")
-public class Ready implements BaseCommand {
-
- @Override
- public String execute(CommandContext commandContext, String[] args) {
- return DubboBootstrap.getInstance().isReady() ? "true" : "false";
- }
+import org.apache.dubbo.common.extension.SPI;
+/**
+ * A probe to indicate whether program is startup
+ * </p>
+ * If one or more spi return false, 'startup' command in dubbo-qos
+ * will return false. This can be extend with custom program and developers
+ * can implement this to customize life cycle.
+ *
+ * @since 3.0
+ */
+@SPI
+public interface StartupProbe {
+ /**
+ * Check if program has been startup
+ *
+ * @return {@link boolean} result of probe
+ */
+ boolean check();
}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapReadinessProbe.java
similarity index 66%
copy from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
copy to dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapReadinessProbe.java
index 06457f6..da80702 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapReadinessProbe.java
@@ -14,19 +14,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.qos.command.impl;
+package org.apache.dubbo.qos.probe.impl;
+import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.qos.command.BaseCommand;
-import org.apache.dubbo.qos.command.CommandContext;
-import org.apache.dubbo.qos.command.annotation.Cmd;
-
-@Cmd(name = "start",summary = "Judge if service has started? ")
-public class Ready implements BaseCommand {
+import org.apache.dubbo.qos.probe.ReadinessProbe;
+@Activate
+public class BootstrapReadinessProbe implements ReadinessProbe {
@Override
- public String execute(CommandContext commandContext, String[] args) {
- return DubboBootstrap.getInstance().isReady() ? "true" : "false";
+ public boolean check() {
+ return !DubboBootstrap.getInstance().isShutdown();
}
-
}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapStartupProbe.java
similarity index 66%
copy from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
copy to dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapStartupProbe.java
index 06457f6..74105e7 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/BootstrapStartupProbe.java
@@ -14,19 +14,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.qos.command.impl;
+package org.apache.dubbo.qos.probe.impl;
+import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.qos.command.BaseCommand;
-import org.apache.dubbo.qos.command.CommandContext;
-import org.apache.dubbo.qos.command.annotation.Cmd;
+import org.apache.dubbo.qos.probe.StartupProbe;
-@Cmd(name = "start",summary = "Judge if service has started? ")
-public class Ready implements BaseCommand {
+@Activate
+public class BootstrapStartupProbe implements StartupProbe {
@Override
- public String execute(CommandContext commandContext, String[] args) {
- return DubboBootstrap.getInstance().isReady() ? "true" : "false";
+ public boolean check() {
+ return DubboBootstrap.getInstance().isStartup();
}
-
}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/ProviderReadinessProbe.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/ProviderReadinessProbe.java
new file mode 100644
index 0000000..5825107
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/probe/impl/ProviderReadinessProbe.java
@@ -0,0 +1,52 @@
+/*
+ * 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.qos.probe.impl;
+
+import org.apache.dubbo.common.extension.Activate;
+import org.apache.dubbo.qos.probe.ReadinessProbe;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+import org.apache.dubbo.rpc.model.ProviderModel;
+import org.apache.dubbo.rpc.model.ServiceRepository;
+
+import java.util.Collection;
+import java.util.List;
+
+@Activate
+public class ProviderReadinessProbe implements ReadinessProbe {
+ private static ServiceRepository serviceRepository = ApplicationModel.getServiceRepository();
+
+ @Override
+ public boolean check() {
+ Collection<ProviderModel> providerModelList = serviceRepository.getExportedServices();
+ if (providerModelList.isEmpty()) {
+ return true;
+ }
+
+ boolean hasService = false;
+ for (ProviderModel providerModel : providerModelList) {
+ List<ProviderModel.RegisterStatedURL> statedUrls = providerModel.getStatedUrl();
+ for (ProviderModel.RegisterStatedURL statedUrl : statedUrls) {
+ if (statedUrl.isRegistered()) {
+ hasService = true;
+ break;
+ }
+ }
+ }
+
+ return hasService;
+ }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/HttpProcessHandler.java b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/HttpProcessHandler.java
index 408e185..07af64d 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/HttpProcessHandler.java
+++ b/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/server/handler/HttpProcessHandler.java
@@ -30,6 +30,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse;
+import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
@@ -52,19 +53,37 @@ public class HttpProcessHandler extends SimpleChannelInboundHandler<HttpRequest>
private static CommandExecutor commandExecutor = new DefaultCommandExecutor();
+ private static FullHttpResponse http(int httpCode, String result) {
+ FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(httpCode)
+ , Unpooled.wrappedBuffer(result.getBytes()));
+ HttpHeaders httpHeaders = response.headers();
+ httpHeaders.set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
+ httpHeaders.set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
+ return response;
+ }
+
+ private static FullHttpResponse http404() {
+ FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND);
+ HttpHeaders httpHeaders = response.headers();
+ httpHeaders.set(HttpHeaderNames.CONTENT_TYPE, "text/plain");
+ httpHeaders.set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
+ return response;
+ }
+
@Override
protected void channelRead0(ChannelHandlerContext ctx, HttpRequest msg) throws Exception {
CommandContext commandContext = HttpCommandDecoder.decode(msg);
// return 404 when fail to construct command context
if (commandContext == null) {
- log.warn("can not found commandContext url: " + msg.getUri());
+ log.warn("can not found commandContext url: " + msg.uri());
FullHttpResponse response = http404();
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
} else {
commandContext.setRemote(ctx.channel());
try {
String result = commandExecutor.execute(commandContext);
- FullHttpResponse response = http200(result);
+ int httpCode = commandContext.getHttpCode();
+ FullHttpResponse response = http(httpCode, result);
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
} catch (NoSuchCommandException ex) {
log.error("can not find commandContext: " + commandContext, ex);
@@ -72,36 +91,10 @@ public class HttpProcessHandler extends SimpleChannelInboundHandler<HttpRequest>
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
} catch (Exception qosEx) {
log.error("execute commandContext: " + commandContext + " got exception", qosEx);
- FullHttpResponse response = http500(qosEx.getMessage());
+ FullHttpResponse response = http(500, qosEx.getMessage());
ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE);
}
}
}
- private static final FullHttpResponse http200(String result) {
- FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
- Unpooled.wrappedBuffer(result.getBytes()));
- HttpHeaders httpHeaders = response.headers();
- httpHeaders.set(HttpHeaders.Names.CONTENT_TYPE, "text/plain");
- httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes());
- return response;
- }
-
- private static final FullHttpResponse http404() {
- FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.NOT_FOUND);
- HttpHeaders httpHeaders = response.headers();
- httpHeaders.set(HttpHeaders.Names.CONTENT_TYPE, "text/plain");
- httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes());
- return response;
- }
-
- private static final FullHttpResponse http500(String errorMessage) {
- FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR
- , Unpooled.wrappedBuffer(errorMessage.getBytes()));
- HttpHeaders httpHeaders = response.headers();
- httpHeaders.set(HttpHeaders.Names.CONTENT_TYPE, "text/plain");
- httpHeaders.set(HttpHeaders.Names.CONTENT_LENGTH, response.content().readableBytes());
- return response;
- }
-
}
diff --git a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.command.BaseCommand b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.command.BaseCommand
index b4f0322..089762d 100644
--- a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.command.BaseCommand
+++ b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.command.BaseCommand
@@ -8,5 +8,7 @@ offline=org.apache.dubbo.qos.command.impl.Offline
offlineApp=org.apache.dubbo.qos.command.impl.OfflineApp
offlineInterface=org.apache.dubbo.qos.command.impl.OfflineInterface
ready=org.apache.dubbo.qos.command.impl.Ready
+startup=org.apache.dubbo.qos.command.impl.Startup
+live=org.apache.dubbo.qos.command.impl.Live
version=org.apache.dubbo.qos.command.impl.Version
publish-metadata=org.apache.dubbo.qos.command.impl.PublishMetadata
diff --git a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.probe.ReadinessProbe b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.probe.ReadinessProbe
new file mode 100644
index 0000000..702ab02
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.probe.ReadinessProbe
@@ -0,0 +1,2 @@
+bootstrap = org.apache.dubbo.qos.probe.impl.BootstrapReadinessProbe
+provider = org.apache.dubbo.qos.probe.impl.ProviderReadinessProbe
\ No newline at end of file
diff --git a/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.probe.StartupProbe b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.probe.StartupProbe
new file mode 100644
index 0000000..1095150
--- /dev/null
+++ b/dubbo-plugin/dubbo-qos/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.qos.probe.StartupProbe
@@ -0,0 +1 @@
+bootstrap = org.apache.dubbo.qos.probe.impl.BootstrapStartupProbe
\ No newline at end of file
diff --git a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java
index 812d5c7..a1e03a2 100644
--- a/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java
+++ b/dubbo-plugin/dubbo-qos/src/test/java/org/apache/dubbo/qos/command/util/CommandHelperTest.java
@@ -18,12 +18,16 @@ package org.apache.dubbo.qos.command.util;
import org.apache.dubbo.qos.command.GreetingCommand;
import org.apache.dubbo.qos.command.impl.Help;
+import org.apache.dubbo.qos.command.impl.Live;
import org.apache.dubbo.qos.command.impl.Ls;
import org.apache.dubbo.qos.command.impl.Offline;
import org.apache.dubbo.qos.command.impl.Online;
+import org.apache.dubbo.qos.command.impl.PublishMetadata;
import org.apache.dubbo.qos.command.impl.Quit;
import org.apache.dubbo.qos.command.impl.Ready;
+import org.apache.dubbo.qos.command.impl.Startup;
import org.apache.dubbo.qos.command.impl.Version;
+
import org.junit.jupiter.api.Test;
import java.util.List;
@@ -47,7 +51,7 @@ public class CommandHelperTest {
List<Class<?>> classes = CommandHelper.getAllCommandClass();
assertThat(classes,
containsInAnyOrder(GreetingCommand.class, Help.class, Ls.class, Offline.class, Online.class, Quit.class,
- Ready.class, Version.class));
+ Live.class, Ready.class, Startup.class, Version.class, PublishMetadata.class));
}
@Test
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/Constants.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/Constants.java
index 1bf4168..eee0665 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/Constants.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/Constants.java
@@ -88,4 +88,14 @@ public interface Constants {
String REGISTRY_RETRY_PERIOD_KEY = "retry.period";
String SESSION_TIMEOUT_KEY = "session";
+
+ /**
+ * To decide the frequency of checking Distributed Service Discovery Registry callback hook (in ms)
+ */
+ String ECHO_POLLING_CYCLE_KEY = "echoPollingCycle";
+
+ /**
+ * Default value for check frequency: 60000 (ms)
+ */
+ int DEFAULT_ECHO_POLLING_CYCLE = 60000;
}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/SelfHostMetaServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/SelfHostMetaServiceDiscovery.java
new file mode 100644
index 0000000..25ab77e
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/SelfHostMetaServiceDiscovery.java
@@ -0,0 +1,290 @@
+/*
+ * 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.client;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.constants.CommonConstants;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+import org.apache.dubbo.common.utils.NamedThreadFactory;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.metadata.InstanceMetadataChangedListener;
+import org.apache.dubbo.metadata.MetadataService;
+import org.apache.dubbo.metadata.RevisionResolver;
+import org.apache.dubbo.metadata.WritableMetadataService;
+import org.apache.dubbo.registry.Constants;
+import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
+import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
+import org.apache.dubbo.registry.client.metadata.MetadataUtils;
+import org.apache.dubbo.rpc.RpcException;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import com.alibaba.fastjson.JSONObject;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+public abstract class SelfHostMetaServiceDiscovery implements ServiceDiscovery {
+
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private URL registryURL;
+
+ /**
+ * Echo check if consumer is still work
+ * echo task may take a lot of time when consumer offline, create a new ScheduledThreadPool
+ */
+ private final ScheduledExecutorService echoCheckExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("Dubbo-Registry-EchoCheck-Consumer"));
+
+ // =================================== Provider side =================================== //
+
+ private ServiceInstance serviceInstance;
+
+ /**
+ * Local {@link ServiceInstance} Metadata's revision
+ */
+ private String lastMetadataRevision;
+
+ // =================================== Consumer side =================================== //
+
+ /**
+ * Local Cache of {@link ServiceInstance} Metadata
+ * <p>
+ * Key - {@link ServiceInstance} ID ( usually ip + port )
+ * Value - Json processed metadata string
+ */
+ private final ConcurrentHashMap<String, String> metadataMap = new ConcurrentHashMap<>();
+
+ /**
+ * Local Cache of {@link ServiceInstance}
+ * <p>
+ * Key - Service Name
+ * Value - List {@link ServiceInstance}
+ */
+ private final ConcurrentHashMap<String, List<ServiceInstance>> cachedServiceInstances = new ConcurrentHashMap<>();
+
+ /**
+ * Local Cache of Service's {@link ServiceInstance} list revision,
+ * used to check if {@link ServiceInstance} list has been updated
+ * <p>
+ * Key - ServiceName
+ * Value - a revision calculate from {@link List} of {@link ServiceInstance}
+ */
+ private final ConcurrentHashMap<String, String> serviceInstanceRevisionMap = new ConcurrentHashMap<>();
+
+ @Override
+ public void initialize(URL registryURL) throws Exception {
+ this.registryURL = registryURL;
+ doInitialize(registryURL);
+ long echoPollingCycle = registryURL.getParameter(Constants.ECHO_POLLING_CYCLE_KEY, Constants.DEFAULT_ECHO_POLLING_CYCLE);
+
+ // Echo check: test if consumer is offline, remove MetadataChangeListener,
+ // reduce the probability of failure when metadata update
+ echoCheckExecutor.scheduleAtFixedRate(() -> {
+ WritableMetadataService metadataService = WritableMetadataService.getDefaultExtension();
+ Map<String, InstanceMetadataChangedListener> listenerMap = metadataService.getInstanceMetadataChangedListenerMap();
+ Iterator<Map.Entry<String, InstanceMetadataChangedListener>> iterator = listenerMap.entrySet().iterator();
+
+ while (iterator.hasNext()) {
+ Map.Entry<String, InstanceMetadataChangedListener> entry = iterator.next();
+ try {
+ entry.getValue().echo(CommonConstants.DUBBO);
+ } catch (RpcException e) {
+ if (logger.isInfoEnabled()) {
+ logger.info("Send echo message to consumer error. Possible cause: consumer is offline.");
+ }
+ iterator.remove();
+ }
+ }
+ }, echoPollingCycle, echoPollingCycle, TimeUnit.MILLISECONDS);
+ }
+
+ @Override
+ public void destroy() throws Exception {
+ doDestroy();
+ metadataMap.clear();
+ serviceInstanceRevisionMap.clear();
+ echoCheckExecutor.shutdown();
+ }
+
+ private void updateMetadata(ServiceInstance serviceInstance) {
+ WritableMetadataService metadataService = WritableMetadataService.getDefaultExtension();
+ String metadataString = JSONObject.toJSONString(serviceInstance.getMetadata());
+ String metadataRevision = RevisionResolver.calRevision(metadataString);
+
+ // check if metadata updated
+ if (!metadataRevision.equalsIgnoreCase(lastMetadataRevision)) {
+ logger.info("Update Service Instance Metadata of DNS registry. Newer metadata: " + metadataString);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Update Service Instance Metadata of DNS registry. Newer metadata: " + metadataString);
+ }
+
+ lastMetadataRevision = metadataRevision;
+
+ // save newest metadata to local
+ metadataService.exportInstanceMetadata(metadataString);
+
+ // notify to consumer
+ Map<String, InstanceMetadataChangedListener> listenerMap = metadataService.getInstanceMetadataChangedListenerMap();
+ Iterator<Map.Entry<String, InstanceMetadataChangedListener>> iterator = listenerMap.entrySet().iterator();
+
+ while (iterator.hasNext()) {
+ Map.Entry<String, InstanceMetadataChangedListener> entry = iterator.next();
+ try {
+ entry.getValue().onEvent(metadataString);
+ } catch (RpcException e) {
+ logger.warn("Notify to consumer error. Possible cause: consumer is offline.");
+ // remove listener if consumer is offline
+ iterator.remove();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void register(ServiceInstance serviceInstance) throws RuntimeException {
+ this.serviceInstance = serviceInstance;
+
+ updateMetadata(serviceInstance);
+
+ doRegister(serviceInstance);
+ }
+
+ @Override
+ public void update(ServiceInstance serviceInstance) throws RuntimeException {
+ this.serviceInstance = serviceInstance;
+
+ updateMetadata(serviceInstance);
+
+ doUpdate(serviceInstance);
+ }
+
+ @Override
+ public void unregister(ServiceInstance serviceInstance) throws RuntimeException {
+ doUnregister(serviceInstance);
+
+ this.serviceInstance = null;
+
+ // notify empty message to consumer
+ WritableMetadataService metadataService = WritableMetadataService.getDefaultExtension();
+ metadataService.exportInstanceMetadata("");
+ metadataService.getInstanceMetadataChangedListenerMap().forEach((consumerId, listener) -> listener.onEvent(""));
+ metadataService.getInstanceMetadataChangedListenerMap().clear();
+ }
+
+ @Override
+ public ServiceInstance getLocalInstance() {
+ return serviceInstance;
+ }
+
+ @Override
+ public URL getUrl() {
+ return registryURL;
+ }
+
+ @SuppressWarnings("unchecked")
+ public final void fillServiceInstance(DefaultServiceInstance serviceInstance) {
+ String hostId = serviceInstance.getId();
+ if (metadataMap.containsKey(hostId)) {
+ // Use cached metadata.
+ // Metadata will be updated by provider callback
+
+ String metadataString = metadataMap.get(hostId);
+ serviceInstance.setMetadata(JSONObject.parseObject(metadataString, Map.class));
+ } else {
+ // refer from MetadataUtils, this proxy is different from the one used to refer exportedURL
+ MetadataService metadataService = MetadataUtils.getMetadataServiceProxy(serviceInstance, this);
+
+ String consumerId = ApplicationModel.getName() + NetUtils.getLocalHost();
+ String metadata = metadataService.getAndListenInstanceMetadata(
+ consumerId, metadataString -> {
+ logger.info("Receive callback: " + metadataString + serviceInstance);
+ if (StringUtils.isEmpty(metadataString)) {
+ // provider is shutdown
+ metadataMap.remove(hostId);
+ } else {
+ metadataMap.put(hostId, metadataString);
+ }
+ });
+ metadataMap.put(hostId, metadata);
+ serviceInstance.setMetadata(JSONObject.parseObject(metadata, Map.class));
+ }
+ }
+
+ public final void notifyListener(String serviceName, ServiceInstancesChangedListener listener, List<ServiceInstance> instances) {
+ String serviceInstanceRevision = RevisionResolver.calRevision(JSONObject.toJSONString(instances));
+ boolean changed = !serviceInstanceRevision.equalsIgnoreCase(
+ serviceInstanceRevisionMap.put(serviceName, serviceInstanceRevision));
+
+ if (logger.isDebugEnabled()) {
+ logger.debug("Poll DNS data. Service Instance changed: " + changed + " Service Name: " + serviceName);
+ }
+
+ if (changed) {
+ List<ServiceInstance> oldServiceInstances = cachedServiceInstances.getOrDefault(serviceName, new LinkedList<>());
+
+ // remove expired invoker
+ Set<ServiceInstance> allServiceInstances = new HashSet<>(oldServiceInstances.size() + instances.size());
+ allServiceInstances.addAll(oldServiceInstances);
+ allServiceInstances.addAll(instances);
+
+ allServiceInstances.removeAll(oldServiceInstances);
+
+ allServiceInstances.forEach(removedServiceInstance -> {
+ MetadataUtils.destroyMetadataServiceProxy(removedServiceInstance, this);
+ });
+
+ cachedServiceInstances.put(serviceName, instances);
+ listener.onEvent(new ServiceInstancesChangedEvent(serviceName, instances));
+ }
+ }
+
+ public void doInitialize(URL registryURL) throws Exception {
+ }
+
+ public void doDestroy() throws Exception {
+ }
+
+ public void doRegister(ServiceInstance serviceInstance) throws RuntimeException {
+
+ }
+
+ public void doUpdate(ServiceInstance serviceInstance) throws RuntimeException {
+
+ }
+
+ public void doUnregister(ServiceInstance serviceInstance) throws RuntimeException {
+
+ }
+
+ /**
+ * UT used only
+ */
+ @Deprecated
+ public final ConcurrentHashMap<String, List<ServiceInstance>> getCachedServiceInstances() {
+ return cachedServiceInstances;
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
index 0321a16..27f66f5 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/MetadataUtils.java
@@ -33,7 +33,11 @@ import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import static org.apache.dubbo.common.constants.CommonConstants.METADATA_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.REMOTE_METADATA_STORAGE_TYPE;
import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.METADATA_SERVICE_URLS_PROPERTY_NAME;
public class MetadataUtils {
@@ -42,6 +46,10 @@ public class MetadataUtils {
public static ConcurrentMap<String, MetadataService> metadataServiceProxies = new ConcurrentHashMap<>();
+ public static ConcurrentMap<String, Invoker<?>> metadataServiceInvokers = new ConcurrentHashMap<>();
+
+ public static ConcurrentMap<String, Lock> metadataServiceLocks = new ConcurrentHashMap<>();
+
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
@@ -72,39 +80,69 @@ public class MetadataUtils {
// store in local
WritableMetadataService.getDefaultExtension().publishServiceDefinition(url);
// send to remote
-// if (REMOTE_METADATA_STORAGE_TYPE.equals(url.getParameter(METADATA_KEY))) {
- getRemoteMetadataService().publishServiceDefinition(url);
-// }
+ if (REMOTE_METADATA_STORAGE_TYPE.equalsIgnoreCase(url.getParameter(METADATA_KEY))) {
+ getRemoteMetadataService().publishServiceDefinition(url);
+ }
+ }
+
+ public static String computeKey(ServiceInstance serviceInstance) {
+ return serviceInstance.getServiceName() + "##" + serviceInstance.getId() + "##" +
+ ServiceInstanceMetadataUtils.getExportedServicesRevision(serviceInstance);
}
public static MetadataService getMetadataServiceProxy(ServiceInstance instance, ServiceDiscovery serviceDiscovery) {
- String key = instance.getServiceName() + "##" +
- ServiceInstanceMetadataUtils.getExportedServicesRevision(instance);
- return metadataServiceProxies.computeIfAbsent(key, k -> {
- MetadataServiceURLBuilder builder = null;
- ExtensionLoader<MetadataServiceURLBuilder> loader
- = ExtensionLoader.getExtensionLoader(MetadataServiceURLBuilder.class);
-
- Map<String, String> metadata = instance.getMetadata();
- // METADATA_SERVICE_URLS_PROPERTY_NAME is a unique key exists only on instances of spring-cloud-alibaba.
- String dubboURLsJSON = metadata.get(METADATA_SERVICE_URLS_PROPERTY_NAME);
- if (StringUtils.isNotEmpty(dubboURLsJSON)) {
- builder = loader.getExtension(SpringCloudMetadataServiceURLBuilder.NAME);
- } else {
- builder = loader.getExtension(StandardMetadataServiceURLBuilder.NAME);
- }
+ String key = computeKey(instance);
+ Lock lock = metadataServiceLocks.computeIfAbsent(key, k -> new ReentrantLock());
+
+ lock.lock();
+ try {
+ return metadataServiceProxies.computeIfAbsent(key, k -> referProxy(k, instance));
+ } finally {
+ lock.unlock();
+ }
+ }
- List<URL> urls = builder.build(instance);
- if (CollectionUtils.isEmpty(urls)) {
- throw new IllegalStateException("You have enabled introspection service discovery mode for instance "
- + instance + ", but no metadata service can build from it.");
+ public static void destroyMetadataServiceProxy(ServiceInstance instance, ServiceDiscovery serviceDiscovery) {
+ String key = computeKey(instance);
+ Lock lock = metadataServiceLocks.computeIfAbsent(key, k -> new ReentrantLock());
+
+ lock.lock();
+ try {
+ if (metadataServiceProxies.containsKey(key)) {
+ metadataServiceProxies.remove(key);
+ Invoker<?> invoker = metadataServiceInvokers.remove(key);
+ invoker.destroy();
}
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ private static MetadataService referProxy(String key, ServiceInstance instance) {
+ MetadataServiceURLBuilder builder = null;
+ ExtensionLoader<MetadataServiceURLBuilder> loader
+ = ExtensionLoader.getExtensionLoader(MetadataServiceURLBuilder.class);
+
+ Map<String, String> metadata = instance.getMetadata();
+ // METADATA_SERVICE_URLS_PROPERTY_NAME is a unique key exists only on instances of spring-cloud-alibaba.
+ String dubboURLsJSON = metadata.get(METADATA_SERVICE_URLS_PROPERTY_NAME);
+ if (metadata.isEmpty() || StringUtils.isEmpty(dubboURLsJSON)) {
+ builder = loader.getExtension(StandardMetadataServiceURLBuilder.NAME);
+ } else {
+ builder = loader.getExtension(SpringCloudMetadataServiceURLBuilder.NAME);
+ }
+
+ List<URL> urls = builder.build(instance);
+ if (CollectionUtils.isEmpty(urls)) {
+ throw new IllegalStateException("You have enabled introspection service discovery mode for instance "
+ + instance + ", but no metadata service can build from it.");
+ }
- // Simply rely on the first metadata url, as stated in MetadataServiceURLBuilder.
- Invoker<MetadataService> invoker = protocol.refer(MetadataService.class, urls.get(0));
+ // Simply rely on the first metadata url, as stated in MetadataServiceURLBuilder.
+ Invoker<MetadataService> invoker = protocol.refer(MetadataService.class, urls.get(0));
+ metadataServiceInvokers.put(key, invoker);
- return proxyFactory.getProxy(invoker);
- });
+ return proxyFactory.getProxy(invoker);
}
public static void saveMetadataURL(URL url) {
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java
index 8adf71a..a7dda4b 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/StandardMetadataServiceURLBuilder.java
@@ -19,19 +19,25 @@ package org.apache.dubbo.registry.client.metadata;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.URLBuilder;
import org.apache.dubbo.common.config.ConfigurationUtils;
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.metadata.MetadataService;
import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.remoting.Constants;
+import org.apache.dubbo.rpc.model.ApplicationModel;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER;
+import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL;
import static org.apache.dubbo.common.constants.CommonConstants.GROUP_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PORT_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.PROTOCOL_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.SIDE_KEY;
import static org.apache.dubbo.common.constants.CommonConstants.TIMEOUT_KEY;
+import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY;
import static org.apache.dubbo.metadata.MetadataConstants.DEFAULT_METADATA_TIMEOUT_VALUE;
import static org.apache.dubbo.metadata.MetadataConstants.METADATA_PROXY_TIMEOUT_KEY;
import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.getMetadataServiceURLsParams;
@@ -43,7 +49,9 @@ import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataU
* @since 2.7.5
*/
public class StandardMetadataServiceURLBuilder implements MetadataServiceURLBuilder {
-
+
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
public static final String NAME = "standard";
/**
@@ -61,24 +69,66 @@ public class StandardMetadataServiceURLBuilder implements MetadataServiceURLBuil
String serviceName = serviceInstance.getServiceName();
String host = serviceInstance.getHost();
- URLBuilder urlBuilder = new URLBuilder();
- for (Map.Entry<String, String> entry : paramsMap.entrySet()) {
- String key = entry.getKey();
- String value = entry.getValue();
- if (key.equals(PORT_KEY)) {
- urlBuilder.setPort(Integer.parseInt(value));
- } else if (key.equals(PROTOCOL_KEY)) {
- urlBuilder.setProtocol(value);
- } else {
- urlBuilder.addParameter(key, value);
- }
+
+ if (paramsMap.isEmpty()) {
+ // ServiceInstance Metadata is empty. Happened when registry not support metadata write.
+ urls.add(generateUrlWithoutMetadata(serviceName, host, serviceInstance.getPort()));
+ } else {
+ urls.add(generateWithMetadata(serviceName, host, paramsMap));
+ }
+
+ return urls;
+ }
+
+ private URL generateWithMetadata(String serviceName, String host, Map<String, String> params) {
+ String protocol = params.get(PROTOCOL_KEY);
+ int port = Integer.parseInt(params.get(PORT_KEY));
+ URLBuilder urlBuilder = new URLBuilder()
+ .setHost(host)
+ .setPort(port)
+ .setProtocol(protocol)
+ .setPath(MetadataService.class.getName())
+ .addParameter(TIMEOUT_KEY, ConfigurationUtils.get(METADATA_PROXY_TIMEOUT_KEY, DEFAULT_METADATA_TIMEOUT_VALUE))
+ .addParameter(SIDE_KEY, CONSUMER);
+
+ // add parameters
+ params.forEach(urlBuilder::addParameter);
+
+ // add the default parameters
+ urlBuilder.addParameter(GROUP_KEY, serviceName);
+ return urlBuilder.build();
+ }
+
+ private URL generateUrlWithoutMetadata(String serviceName, String host, Integer instancePort) {
+ Integer port = ApplicationModel.getApplicationConfig().getMetadataServicePort();
+ if (port == null || port < 1) {
+ logger.warn("Metadata Service Port is not provided, since DNS is not able to negotiate the metadata port " +
+ "between Provider and Consumer, will try to use instance port as the default metadata port.");
+ port = instancePort;
}
- urlBuilder.setHost(host).setPath(MetadataService.class.getName())
+
+ if (port == null || port < 1) {
+ String message = "Metadata Service Port should be specified for consumer. " +
+ "Please set dubbo.application.metadataServicePort and " +
+ "make sure it has been set on provider side. " +
+ "ServiceName: " + serviceName + " Host: " + host;
+ throw new IllegalStateException(message);
+ }
+
+ URLBuilder urlBuilder = new URLBuilder()
+ .setHost(host)
+ .setPort(port)
+ .setProtocol(DUBBO_PROTOCOL)
+ .setPath(MetadataService.class.getName())
.addParameter(TIMEOUT_KEY, ConfigurationUtils.get(METADATA_PROXY_TIMEOUT_KEY, DEFAULT_METADATA_TIMEOUT_VALUE))
+ .addParameter(Constants.RECONNECT_KEY, false)
.addParameter(SIDE_KEY, CONSUMER)
- .addParameter(GROUP_KEY, serviceName);
+ .addParameter(GROUP_KEY, serviceName)
+ .addParameter(VERSION_KEY, MetadataService.VERSION);
- urls.add(urlBuilder.build());
- return urls;
+ // add ServiceInstance Metadata notify support
+ urlBuilder.addParameter("getAndListenInstanceMetadata.1.callback", true);
+
+ return urlBuilder.build();
}
}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
index e31745a..fdecb76 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/InMemoryWritableMetadataService.java
@@ -21,6 +21,7 @@ import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
import org.apache.dubbo.common.utils.CollectionUtils;
import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.metadata.InstanceMetadataChangedListener;
import org.apache.dubbo.metadata.MetadataInfo;
import org.apache.dubbo.metadata.MetadataInfo.ServiceInfo;
import org.apache.dubbo.metadata.MetadataService;
@@ -82,9 +83,12 @@ public class InMemoryWritableMetadataService implements WritableMetadataService
URL metadataServiceURL;
ConcurrentMap<String, MetadataInfo> metadataInfos;
final Semaphore metadataSemaphore = new Semaphore(0);
-
final Map<String, Set<String>> serviceToAppsMapping = new HashMap<>();
+ String instanceMetadata;
+ ConcurrentMap<String, InstanceMetadataChangedListener> instanceMetadataChangedListenerMap = new ConcurrentHashMap<>();
+
+
// ==================================================================================== //
// =================================== Subscription =================================== //
@@ -241,6 +245,22 @@ public class InMemoryWritableMetadataService implements WritableMetadataService
}
@Override
+ public void exportInstanceMetadata(String metadata) {
+ this.instanceMetadata = metadata;
+ }
+
+ @Override
+ public Map<String, InstanceMetadataChangedListener> getInstanceMetadataChangedListenerMap() {
+ return instanceMetadataChangedListenerMap;
+ }
+
+ @Override
+ public String getAndListenInstanceMetadata(String consumerId, InstanceMetadataChangedListener listener) {
+ instanceMetadataChangedListenerMap.put(consumerId, listener);
+ return instanceMetadata;
+ }
+
+ @Override
public MetadataInfo getDefaultMetadataInfo() {
if (CollectionUtils.isEmptyMap(metadataInfos)) {
return null;
@@ -379,5 +399,4 @@ public class InMemoryWritableMetadataService implements WritableMetadataService
return o1.toFullString().compareTo(o2.toFullString());
}
}
-
}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java
index 25bc03c..bca273b 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/metadata/store/RemoteMetadataServiceImpl.java
@@ -60,15 +60,22 @@ public class RemoteMetadataServiceImpl {
SubscriberMetadataIdentifier identifier = new SubscriberMetadataIdentifier(serviceName, metadataInfo.calAndGetRevision());
metadataInfo.calAndGetRevision();
metadataInfo.getExtendParams().put(REGISTRY_CLUSTER_KEY, registryCluster);
- MetadataReport metadataReport = getMetadataReports().get(registryCluster);
- if (metadataReport == null) {
- metadataReport = getMetadataReports().entrySet().iterator().next().getValue();
+ if (getMetadataReports().size() > 0) {
+ MetadataReport metadataReport = getMetadataReports().get(registryCluster);
+ if (metadataReport == null) {
+ metadataReport = getMetadataReports().entrySet().iterator().next().getValue();
+ }
+ logger.info("Publishing metadata to " + metadataReport.getClass().getSimpleName());
+ if (logger.isDebugEnabled()) {
+ logger.debug(metadataInfo.toString());
+ }
+ metadataReport.publishAppMetadata(identifier, metadataInfo);
+ } else {
+ if (logger.isInfoEnabled()) {
+ logger.info("Remote Metadata Report Server not hasn't been configured. " +
+ "Only publish Metadata to local.");
+ }
}
- logger.info("Publishing metadata to " + metadataReport.getClass().getSimpleName());
- if (logger.isDebugEnabled()) {
- logger.debug(metadataInfo.toString());
- }
- metadataReport.publishAppMetadata(identifier, metadataInfo);
metadataInfo.markReported();
}
});
@@ -80,6 +87,8 @@ public class RemoteMetadataServiceImpl {
String registryCluster = instance.getExtendParams().get(REGISTRY_CLUSTER_KEY);
+ checkRemoteConfigured();
+
MetadataReport metadataReport = getMetadataReports().get(registryCluster);
if (metadataReport == null) {
metadataReport = getMetadataReports().entrySet().iterator().next().getValue();
@@ -87,8 +96,20 @@ public class RemoteMetadataServiceImpl {
return metadataReport.getAppMetadata(identifier, instance.getExtendParams());
}
+ private void checkRemoteConfigured() {
+ if (getMetadataReports().size() == 0) {
+ String msg = "Remote Metadata Report Server not hasn't been configured. " +
+ "Unable to get Metadata from remote!";
+ logger.error(msg);
+ throw new IllegalStateException(msg);
+ }
+ }
+
public void publishServiceDefinition(URL url) {
String side = url.getSide();
+
+ checkRemoteConfigured();
+
if (PROVIDER_SIDE.equalsIgnoreCase(side)) {
//TODO, the params part is duplicate with that stored by exportURL(url), can be further optimized in the future.
publishProvider(url);
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationClusterInvoker.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationClusterInvoker.java
index 91fa942..2efae4c 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationClusterInvoker.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/client/migration/MigrationClusterInvoker.java
@@ -47,4 +47,4 @@ public interface MigrationClusterInvoker<T> extends ClusterInvoker<T> {
void refreshServiceDiscoveryInvokerOnMappingCallback(boolean forceMigrate);
void reRefer(URL newSubscribeUrl);
-}
+}
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/InMemoryServiceDiscovery.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/InMemoryServiceDiscovery.java
index 93043ba..20eeed3 100644
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/InMemoryServiceDiscovery.java
+++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/client/InMemoryServiceDiscovery.java
@@ -44,6 +44,8 @@ public class InMemoryServiceDiscovery implements ServiceDiscovery {
private ServiceInstance serviceInstance;
+ private URL registryURL;
+
@Override
public Set<String> getServices() {
return repository.keySet();
@@ -71,6 +73,11 @@ public class InMemoryServiceDiscovery implements ServiceDiscovery {
}
@Override
+ public URL getUrl() {
+ return registryURL;
+ }
+
+ @Override
public ServiceInstance getLocalInstance() {
return serviceInstance;
}
@@ -104,7 +111,7 @@ public class InMemoryServiceDiscovery implements ServiceDiscovery {
@Override
public void initialize(URL registryURL) throws Exception {
-
+ this.registryURL = registryURL;
}
@Override
diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/support/ServiceOrientedRegistryTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/support/ServiceOrientedRegistryTest.java
index e65a271..bc0906f 100644
--- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/support/ServiceOrientedRegistryTest.java
+++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/support/ServiceOrientedRegistryTest.java
@@ -18,9 +18,11 @@ package org.apache.dubbo.registry.support;
import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.utils.CollectionUtils;
+import org.apache.dubbo.config.ApplicationConfig;
import org.apache.dubbo.metadata.WritableMetadataService;
import org.apache.dubbo.registry.NotifyListener;
import org.apache.dubbo.registry.client.ServiceDiscoveryRegistry;
+import org.apache.dubbo.rpc.model.ApplicationModel;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -38,6 +40,7 @@ import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER_SIDE;
import static org.apache.dubbo.common.constants.RegistryConstants.REGISTRY_TYPE_KEY;
import static org.apache.dubbo.common.constants.RegistryConstants.SERVICE_REGISTRY_TYPE;
import static org.apache.dubbo.common.constants.RegistryConstants.SUBSCRIBED_SERVICE_NAMES_KEY;
+import static org.apache.dubbo.rpc.Constants.ID_KEY;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -51,6 +54,7 @@ public class ServiceOrientedRegistryTest {
private static final URL registryURL = valueOf("in-memory://localhost:12345")
.addParameter(REGISTRY_TYPE_KEY, SERVICE_REGISTRY_TYPE)
+ .addParameter(ID_KEY, "org.apache.dubbo.config.RegistryConfig#0")
.addParameter(SUBSCRIBED_SERVICE_NAMES_KEY, "a, b , c,d,e ,");
private static final String SERVICE_INTERFACE = "org.apache.dubbo.metadata.MetadataService";
@@ -81,6 +85,7 @@ public class ServiceOrientedRegistryTest {
registry = ServiceDiscoveryRegistry.create(registryURL);
metadataService = WritableMetadataService.getDefaultExtension();
notifyListener = new MyNotifyListener();
+ ApplicationModel.getConfigManager().setApplication(new ApplicationConfig("Test"));
}
@Test
diff --git a/dubbo-registry/dubbo-registry-dns/pom.xml b/dubbo-registry/dubbo-registry-dns/pom.xml
new file mode 100644
index 0000000..54e526c
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-dns/pom.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ 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:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-registry</artifactId>
+ <version>${revision}</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>dubbo-registry-dns</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The DNS registry module of Dubbo project</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-registry-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-common</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-all</artifactId>
+ </dependency>
+
+ <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-rpc-dubbo</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-remoting-netty4</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-registry-multicast</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/DNSRegistry.java b/dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/DNSRegistry.java
new file mode 100644
index 0000000..79fa0cd
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/DNSRegistry.java
@@ -0,0 +1,58 @@
+/*
+ * 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.dns;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.NotifyListener;
+import org.apache.dubbo.registry.support.FailbackRegistry;
+
+/**
+ * Empty implements for DNS <br/>
+ * DNS only support `Service Discovery` mode register <br/>
+ * Used to compat past version like 2.6.x, 2.7.x with interface level register <br/>
+ * {@link DNSServiceDiscovery} is the real implementation of DNS
+ */
+public class DNSRegistry extends FailbackRegistry {
+ public DNSRegistry(URL url) {
+ super(url);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public void doRegister(URL url) {
+
+ }
+
+ @Override
+ public void doUnregister(URL url) {
+
+ }
+
+ @Override
+ public void doSubscribe(URL url, NotifyListener listener) {
+
+ }
+
+ @Override
+ public void doUnsubscribe(URL url, NotifyListener listener) {
+
+ }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java b/dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/DNSRegistryFactory.java
similarity index 61%
copy from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
copy to dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/DNSRegistryFactory.java
index 06457f6..870485e 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
+++ b/dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/DNSRegistryFactory.java
@@ -14,19 +14,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.qos.command.impl;
+package org.apache.dubbo.registry.dns;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.qos.command.BaseCommand;
-import org.apache.dubbo.qos.command.CommandContext;
-import org.apache.dubbo.qos.command.annotation.Cmd;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.Registry;
+import org.apache.dubbo.registry.support.AbstractRegistryFactory;
-@Cmd(name = "start",summary = "Judge if service has started? ")
-public class Ready implements BaseCommand {
+public class DNSRegistryFactory extends AbstractRegistryFactory {
@Override
- public String execute(CommandContext commandContext, String[] args) {
- return DubboBootstrap.getInstance().isReady() ? "true" : "false";
+ protected String createRegistryCacheKey(URL url) {
+ return url.toFullString();
}
+ @Override
+ protected Registry createRegistry(URL url) {
+ return new DNSRegistry(url);
+ }
}
diff --git a/dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/DNSServiceDiscovery.java b/dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/DNSServiceDiscovery.java
new file mode 100644
index 0000000..a4d30d6
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/DNSServiceDiscovery.java
@@ -0,0 +1,152 @@
+/*
+ * 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.dns;
+
+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.NamedThreadFactory;
+import org.apache.dubbo.registry.client.DefaultServiceInstance;
+import org.apache.dubbo.registry.client.SelfHostMetaServiceDiscovery;
+import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
+import org.apache.dubbo.registry.dns.util.DNSClientConst;
+import org.apache.dubbo.registry.dns.util.DNSResolver;
+import org.apache.dubbo.registry.dns.util.ResolveResult;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+public class DNSServiceDiscovery extends SelfHostMetaServiceDiscovery {
+
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ /**
+ * DNS properties
+ */
+
+ private String addressPrefix;
+ private String addressSuffix;
+ private long pollingCycle;
+ private DNSResolver dnsResolver;
+
+ /**
+ * Polling task ScheduledFuture, used to stop task when destroy
+ */
+ private final ConcurrentHashMap<String, ScheduledFuture<?>> pollingExecutorMap = new ConcurrentHashMap<>();
+
+ /**
+ * Polling check provider ExecutorService
+ */
+ private ScheduledExecutorService pollingExecutorService;
+
+ @Override
+ public void doInitialize(URL registryURL) throws Exception {
+ this.addressPrefix = registryURL.getParameter(DNSClientConst.ADDRESS_PREFIX, "");
+ this.addressSuffix = registryURL.getParameter(DNSClientConst.ADDRESS_SUFFIX, "");
+ this.pollingCycle = registryURL.getParameter(DNSClientConst.DNS_POLLING_CYCLE, DNSClientConst.DEFAULT_DNS_POLLING_CYCLE);
+
+ String nameserver = registryURL.getHost();
+ int port = registryURL.getPort();
+ int maxQueriesPerResolve = registryURL.getParameter(DNSClientConst.MAX_QUERIES_PER_RESOLVE, 10);
+ this.dnsResolver = new DNSResolver(nameserver, port, maxQueriesPerResolve);
+
+
+ int scheduledThreadPoolSize = registryURL.getParameter(DNSClientConst.DNS_POLLING_POOL_SIZE_KEY, DNSClientConst.DEFAULT_DNS_POLLING_POOL_SIZE);
+
+ // polling task may take a lot of time, create a new ScheduledThreadPool
+ pollingExecutorService = Executors.newScheduledThreadPool(scheduledThreadPoolSize, new NamedThreadFactory("Dubbo-DNS-Poll"));
+
+ }
+
+ @Override
+ public void doDestroy() throws Exception {
+ dnsResolver.destroy();
+ pollingExecutorMap.forEach((serviceName, scheduledFuture) -> scheduledFuture.cancel(true));
+ pollingExecutorMap.clear();
+ pollingExecutorService.shutdown();
+ }
+
+ @Override
+ public Set<String> getServices() {
+ // it is impossible for dns to discover service names
+ return Collections.singleton("Unsupported Method");
+ }
+
+ @Override
+ public List<ServiceInstance> getInstances(String serviceName) throws NullPointerException {
+
+ String serviceAddress = addressPrefix + serviceName + addressSuffix;
+
+ ResolveResult resolveResult = dnsResolver.resolve(serviceAddress);
+
+ return toServiceInstance(serviceName, resolveResult);
+ }
+
+ @Override
+ public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException {
+ listener.getServiceNames().forEach(serviceName -> {
+ ScheduledFuture<?> scheduledFuture = pollingExecutorService.scheduleAtFixedRate(() -> {
+ List<ServiceInstance> instances = getInstances(serviceName);
+ instances.sort(Comparator.comparingInt(ServiceInstance::hashCode));
+ notifyListener(serviceName, listener, instances);
+ },
+ pollingCycle, pollingCycle, TimeUnit.MILLISECONDS);
+
+ pollingExecutorMap.put(serviceName, scheduledFuture);
+ });
+ }
+
+ /**
+ * UT used only
+ */
+ @Deprecated
+ public void setDnsResolver(DNSResolver dnsResolver) {
+ this.dnsResolver = dnsResolver;
+ }
+
+ private List<ServiceInstance> toServiceInstance(String serviceName, ResolveResult resolveResult) {
+
+ int port;
+
+ if (resolveResult.getPort().size() > 0) {
+ // use first as default
+ port = resolveResult.getPort().get(0);
+ } else {
+ // not support SRV record
+ port = 20880;
+ }
+
+ List<ServiceInstance> instanceList = new LinkedList<>();
+
+ for (String host : resolveResult.getHostnameList()) {
+ DefaultServiceInstance serviceInstance = new DefaultServiceInstance(serviceName, host, port);
+ fillServiceInstance(serviceInstance);
+ instanceList.add(serviceInstance);
+ }
+
+ return instanceList;
+ }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java b/dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/DNSServiceDiscoveryFactory.java
similarity index 61%
copy from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
copy to dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/DNSServiceDiscoveryFactory.java
index 06457f6..57eba84 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
+++ b/dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/DNSServiceDiscoveryFactory.java
@@ -14,19 +14,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.qos.command.impl;
+package org.apache.dubbo.registry.dns;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.qos.command.BaseCommand;
-import org.apache.dubbo.qos.command.CommandContext;
-import org.apache.dubbo.qos.command.annotation.Cmd;
-
-@Cmd(name = "start",summary = "Judge if service has started? ")
-public class Ready implements BaseCommand {
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory;
+import org.apache.dubbo.registry.client.ServiceDiscovery;
+public class DNSServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory {
@Override
- public String execute(CommandContext commandContext, String[] args) {
- return DubboBootstrap.getInstance().isReady() ? "true" : "false";
+ protected ServiceDiscovery createDiscovery(URL registryURL) {
+ return new DNSServiceDiscovery();
}
-
}
diff --git a/dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/util/DNSClientConst.java b/dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/util/DNSClientConst.java
new file mode 100644
index 0000000..0fc8ada
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/util/DNSClientConst.java
@@ -0,0 +1,47 @@
+/*
+ * 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.dns.util;
+
+public class DNSClientConst {
+
+ public final static String ADDRESS_PREFIX = "addressPrefix";
+
+ public final static String ADDRESS_SUFFIX = "addressSuffix";
+
+ public final static String MAX_QUERIES_PER_RESOLVE = "maxQueriesPerResolve";
+
+ /**
+ * To decide the frequency of execute DNS poll (in ms)
+ */
+ public final static String DNS_POLLING_CYCLE = "dnsPollingCycle";
+
+ /**
+ * Default value for check frequency: 60000 (ms)
+ */
+ public final static int DEFAULT_DNS_POLLING_CYCLE = 60000;
+
+ /**
+ * To decide how many threads used to execute DNS poll
+ */
+ public final static String DNS_POLLING_POOL_SIZE_KEY = "dnsPollingPoolSize";
+
+ /**
+ * Default value for DNS pool thread: 1
+ */
+ public final static int DEFAULT_DNS_POLLING_POOL_SIZE = 1;
+
+}
diff --git a/dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/util/DNSResolver.java b/dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/util/DNSResolver.java
new file mode 100644
index 0000000..7745873
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/util/DNSResolver.java
@@ -0,0 +1,120 @@
+/*
+ * 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.dns.util;
+
+import org.apache.dubbo.common.logger.Logger;
+import org.apache.dubbo.common.logger.LoggerFactory;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.AddressedEnvelope;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioDatagramChannel;
+import io.netty.handler.codec.dns.DefaultDnsQuestion;
+import io.netty.handler.codec.dns.DnsRawRecord;
+import io.netty.handler.codec.dns.DnsRecordType;
+import io.netty.handler.codec.dns.DnsResponse;
+import io.netty.handler.codec.dns.DnsSection;
+import io.netty.resolver.ResolvedAddressTypes;
+import io.netty.resolver.dns.DnsNameResolver;
+import io.netty.resolver.dns.DnsNameResolverBuilder;
+import io.netty.util.concurrent.Future;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.UnknownHostException;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import static io.netty.resolver.dns.DnsServerAddresses.sequential;
+
+public class DNSResolver {
+
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final DnsNameResolver resolver;
+
+ private static final EventLoopGroup GROUP = new NioEventLoopGroup(1);
+
+ public DNSResolver(String nameserver, int port, int maxQueriesPerResolve) {
+ this.resolver = newResolver(nameserver, port, maxQueriesPerResolve);
+ }
+
+ public ResolveResult resolve(String path) {
+ ResolveResult recordList = new ResolveResult();
+
+ try {
+ Future<List<InetAddress>> hostFuture = resolver.resolveAll(path);
+ Future<AddressedEnvelope<DnsResponse, InetSocketAddress>> srvFuture =
+ resolver.query(new DefaultDnsQuestion(path, DnsRecordType.SRV));
+
+ try {
+ recordList.getHostnameList()
+ .addAll(hostFuture
+ .sync().getNow()
+ .stream()
+ .map(InetAddress::getHostAddress)
+ .collect(Collectors.toList()));
+
+ DnsResponse srvResponse = srvFuture.sync().getNow().content();
+ for (int i = 0; i < srvResponse.count(DnsSection.ANSWER); i++) {
+ DnsRawRecord record = srvResponse.recordAt(DnsSection.ANSWER, i);
+ ByteBuf buf = record.content();
+ // Priority
+ buf.readUnsignedShort();
+ // Weight
+ buf.readUnsignedShort();
+ // Port
+ int port = buf.readUnsignedShort();
+ recordList.getPort().add(port);
+ }
+
+ } catch (InterruptedException e) {
+ logger.warn("Waiting DNS resolve interrupted. " + e.getLocalizedMessage());
+ }
+ } catch (Throwable t) {
+ if (t instanceof UnknownHostException) {
+ if (logger.isInfoEnabled()) {
+ logger.info(t.getLocalizedMessage());
+ }
+ } else {
+ logger.error(t.getLocalizedMessage());
+ }
+ }
+
+
+ return recordList;
+ }
+
+ public void destroy() {
+ resolver.close();
+ }
+
+ private static DnsNameResolver newResolver(String nameserver, int port, int maxQueriesPerResolve) {
+ return new DnsNameResolverBuilder(GROUP.next())
+ .channelType(NioDatagramChannel.class)
+ .maxQueriesPerResolve(maxQueriesPerResolve)
+ .decodeIdn(true)
+ .optResourceEnabled(false)
+ .ndots(1)
+ .resolvedAddressTypes(ResolvedAddressTypes.IPV4_PREFERRED)
+ // ignore cache
+ .ttl(0, 1)
+ .nameServerProvider((hostname) -> sequential(new InetSocketAddress(nameserver, port)).stream())
+ .build();
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/util/ResolveResult.java b/dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/util/ResolveResult.java
new file mode 100644
index 0000000..a02b122
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-dns/src/main/java/org/apache/dubbo/registry/dns/util/ResolveResult.java
@@ -0,0 +1,66 @@
+/*
+ * 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.dns.util;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Objects;
+
+public class ResolveResult {
+
+ private List<String> hostnameList = new LinkedList<>();
+
+ private List<Integer> port = new LinkedList<>();
+
+ public List<String> getHostnameList() {
+ return hostnameList;
+ }
+
+ public void setHostnameList(List<String> hostnameList) {
+ this.hostnameList = hostnameList;
+ }
+
+ public List<Integer> getPort() {
+ return port;
+ }
+
+ public void setPort(List<Integer> port) {
+ this.port = port;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ ResolveResult that = (ResolveResult) o;
+ return Objects.equals(hostnameList, that.hostnameList) &&
+ Objects.equals(port, that.port);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(hostnameList, port);
+ }
+
+ @Override
+ public String toString() {
+ return "ResolveResult{" +
+ "hostnameList=" + hostnameList +
+ ", port=" + port +
+ '}';
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-dns/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory b/dubbo-registry/dubbo-registry-dns/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..69ca007
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-dns/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+dns=org.apache.dubbo.registry.dns.DNSRegistryFactory
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-dns/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery b/dubbo-registry/dubbo-registry-dns/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
new file mode 100644
index 0000000..3adb9f4
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-dns/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
@@ -0,0 +1 @@
+dns=org.apache.dubbo.registry.dns.DNSServiceDiscovery
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-dns/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory b/dubbo-registry/dubbo-registry-dns/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
new file mode 100644
index 0000000..900e78c
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-dns/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
@@ -0,0 +1 @@
+dns=org.apache.dubbo.registry.dns.DNSServiceDiscoveryFactory
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-dns/src/test/java/org/apache/dubbo/registry/dns/DNSServiceDiscoveryTest.java b/dubbo-registry/dubbo-registry-dns/src/test/java/org/apache/dubbo/registry/dns/DNSServiceDiscoveryTest.java
new file mode 100644
index 0000000..cc6610b
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-dns/src/test/java/org/apache/dubbo/registry/dns/DNSServiceDiscoveryTest.java
@@ -0,0 +1,191 @@
+/*
+ * 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.dns;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.NetUtils;
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.config.ArgumentConfig;
+import org.apache.dubbo.config.MethodConfig;
+import org.apache.dubbo.config.ProtocolConfig;
+import org.apache.dubbo.config.RegistryConfig;
+import org.apache.dubbo.config.ServiceConfig;
+import org.apache.dubbo.metadata.InstanceMetadataChangedListener;
+import org.apache.dubbo.metadata.MetadataService;
+import org.apache.dubbo.metadata.WritableMetadataService;
+import org.apache.dubbo.registry.Constants;
+import org.apache.dubbo.registry.client.DefaultServiceInstance;
+import org.apache.dubbo.registry.client.ServiceDiscovery;
+import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
+import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
+import org.apache.dubbo.registry.dns.util.DNSClientConst;
+import org.apache.dubbo.registry.dns.util.DNSResolver;
+import org.apache.dubbo.registry.dns.util.ResolveResult;
+import org.apache.dubbo.rpc.model.ApplicationModel;
+
+import com.alibaba.fastjson.JSONObject;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static org.apache.dubbo.common.constants.CommonConstants.DUBBO_PROTOCOL;
+import static org.apache.dubbo.metadata.MetadataConstants.METADATA_PROXY_TIMEOUT_KEY;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class DNSServiceDiscoveryTest {
+
+ @BeforeEach
+ public void setup() {
+ ApplicationModel.reset();
+ ApplicationConfig applicationConfig = new ApplicationConfig("Test");
+ ApplicationModel.getConfigManager().setApplication(applicationConfig);
+ }
+
+ @AfterEach
+ public void destroy() {
+ ApplicationModel.reset();
+ }
+
+ @Test
+ public void testProvider() throws Exception {
+ ServiceDiscovery dnsServiceDiscovery = new DNSServiceDiscovery();
+
+ URL registryURL = URL.valueOf("dns://");
+ dnsServiceDiscovery.initialize(registryURL);
+
+ assertEquals(registryURL, dnsServiceDiscovery.getUrl());
+
+ ServiceInstance serviceInstance = new DefaultServiceInstance("TestService", "localhost", 12345);
+ serviceInstance.getMetadata().put("a", "b");
+
+ dnsServiceDiscovery.register(serviceInstance);
+
+ WritableMetadataService metadataService = WritableMetadataService.getDefaultExtension();
+ InstanceMetadataChangedListener changeListener = Mockito.mock(InstanceMetadataChangedListener.class);
+
+ String metadataString = metadataService
+ .getAndListenInstanceMetadata("test", changeListener);
+
+ assertEquals(JSONObject.toJSONString(serviceInstance.getMetadata()), metadataString);
+ assertEquals(serviceInstance, dnsServiceDiscovery.getLocalInstance());
+
+ dnsServiceDiscovery.unregister(serviceInstance);
+
+ Mockito.verify(changeListener, Mockito.times(1)).onEvent(Mockito.any());
+
+ metadataService.getInstanceMetadataChangedListenerMap().clear();
+ metadataService.exportInstanceMetadata(null);
+
+ dnsServiceDiscovery.destroy();
+
+ }
+
+ @Test
+ public void testConsumer() throws Exception {
+ DNSServiceDiscovery dnsServiceDiscovery = new DNSServiceDiscovery();
+
+ URL registryURL = URL.valueOf("dns://")
+ .addParameter(DNSClientConst.DNS_POLLING_CYCLE, 100)
+ .addParameter(Constants.ECHO_POLLING_CYCLE_KEY, 100);
+ ApplicationModel.getEnvironment().getAppExternalConfigurationMap()
+ .put(METADATA_PROXY_TIMEOUT_KEY, String.valueOf(500));
+ dnsServiceDiscovery.initialize(registryURL);
+
+ WritableMetadataService metadataService = WritableMetadataService.getDefaultExtension();
+ ServiceInstance serviceInstance = new DefaultServiceInstance("TestService", "localhost", 12345);
+ serviceInstance.getMetadata().put("a", "b");
+
+ dnsServiceDiscovery.register(serviceInstance);
+
+ int port = NetUtils.getAvailablePort();
+ ApplicationModel.getApplicationConfig().setMetadataServicePort(port);
+
+ WritableMetadataService spiedMetadataService = Mockito.spy(metadataService);
+
+ ServiceConfig<MetadataService> serviceConfig = exportMockMetadataService(spiedMetadataService, port);
+
+ DNSResolver dnsResolver = Mockito.mock(DNSResolver.class);
+ ResolveResult resolveResult = new ResolveResult();
+ resolveResult.getHostnameList().add("127.0.0.1");
+ Mockito.when(dnsResolver.resolve("Test.Service.")).thenReturn(resolveResult);
+ dnsServiceDiscovery.setDnsResolver(dnsResolver);
+
+ List<ServiceInstance> serviceInstances = dnsServiceDiscovery.getInstances("Test.Service.");
+ assertEquals("b", serviceInstances.get(0).getMetadata("a"));
+
+ Set<String> serviceNames = new HashSet<>();
+ serviceNames.add("Test.Service.");
+ ServiceInstancesChangedListener changedListener = Mockito.spy(new ServiceInstancesChangedListener(serviceNames, null));
+ Mockito.doNothing().when(changedListener).onEvent(Mockito.any());
+
+ serviceInstance.getMetadata().put("a", "c");
+ dnsServiceDiscovery.update(serviceInstance);
+
+ serviceInstances = dnsServiceDiscovery.getInstances("Test.Service.");
+ assertEquals("c", serviceInstances.get(0).getMetadata("a"));
+
+ dnsServiceDiscovery.addServiceInstancesChangedListener(changedListener);
+ ArgumentCaptor<ServiceInstancesChangedEvent> argument = ArgumentCaptor.forClass(ServiceInstancesChangedEvent.class);
+ Mockito.verify(changedListener, Mockito.timeout(1000)).onEvent(argument.capture());
+ assertEquals("c", argument.getValue().getServiceInstances().get(0).getMetadata("a"));
+
+ Mockito.when(dnsResolver.resolve("Test.Service.")).thenReturn(new ResolveResult());
+
+ Thread.sleep(1000);
+ assertTrue(dnsServiceDiscovery.getCachedServiceInstances().get("Test.Service.").isEmpty());
+
+ metadataService.exportInstanceMetadata(null);
+ metadataService.getInstanceMetadataChangedListenerMap().clear();
+ serviceConfig.unexport();
+
+ dnsServiceDiscovery.destroy();
+ ApplicationModel.getEnvironment().getAppExternalConfigurationMap()
+ .remove(METADATA_PROXY_TIMEOUT_KEY, String.valueOf(100));
+ }
+
+ private ServiceConfig<MetadataService> exportMockMetadataService(MetadataService metadataService, int port) {
+ ServiceConfig<MetadataService> serviceConfig = new ServiceConfig<>();
+ serviceConfig.setProtocol(new ProtocolConfig(DUBBO_PROTOCOL, port));
+ serviceConfig.setRegistry(new RegistryConfig("239.255.255.255", "multicast"));
+ serviceConfig.setInterface(MetadataService.class);
+ serviceConfig.setRef(metadataService);
+ serviceConfig.setGroup("Test.Service.");
+ serviceConfig.setVersion(MetadataService.VERSION);
+ MethodConfig methodConfig = new MethodConfig();
+ methodConfig.setName("getAndListenInstanceMetadata");
+
+ ArgumentConfig argumentConfig = new ArgumentConfig();
+ argumentConfig.setIndex(1);
+ argumentConfig.setCallback(true);
+
+ methodConfig.setArguments(Collections.singletonList(argumentConfig));
+ serviceConfig.setMethods(Collections.singletonList(methodConfig));
+
+ serviceConfig.export();
+
+ return serviceConfig;
+ }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java b/dubbo-registry/dubbo-registry-dns/src/test/java/org/apache/dubbo/registry/dns/util/DNSResolverTest.java
similarity index 59%
copy from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
copy to dubbo-registry/dubbo-registry-dns/src/test/java/org/apache/dubbo/registry/dns/util/DNSResolverTest.java
index 06457f6..8624a7f 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
+++ b/dubbo-registry/dubbo-registry-dns/src/test/java/org/apache/dubbo/registry/dns/util/DNSResolverTest.java
@@ -14,19 +14,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.qos.command.impl;
+package org.apache.dubbo.registry.dns.util;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.qos.command.BaseCommand;
-import org.apache.dubbo.qos.command.CommandContext;
-import org.apache.dubbo.qos.command.annotation.Cmd;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
-@Cmd(name = "start",summary = "Judge if service has started? ")
-public class Ready implements BaseCommand {
+public class DNSResolverTest {
- @Override
- public String execute(CommandContext commandContext, String[] args) {
- return DubboBootstrap.getInstance().isReady() ? "true" : "false";
- }
+ @Test
+ public void testResolve() {
+ DNSResolver dnsResolver = new DNSResolver("8.8.8.8", 53, 1);
+ ResolveResult resolve = dnsResolver.resolve("aliyun.com");
+ Assertions.assertTrue(resolve.getHostnameList().size() > 0);
+ resolve = dnsResolver.resolve("unknowhost.unknowhost.unknowhost");
+ Assertions.assertEquals(0, resolve.getHostnameList().size());
+ }
}
diff --git a/dubbo-registry/dubbo-registry-kubernetes/pom.xml b/dubbo-registry/dubbo-registry-kubernetes/pom.xml
new file mode 100644
index 0000000..799e45e
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-kubernetes/pom.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ 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">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-registry</artifactId>
+ <version>${revision}</version>
+ <relativePath>../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>dubbo-registry-kubernetes</artifactId>
+ <packaging>jar</packaging>
+ <name>${project.artifactId}</name>
+ <description>The Kubernetes registry module of Dubbo project</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-registry-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.dubbo</groupId>
+ <artifactId>dubbo-common</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>io.fabric8</groupId>
+ <artifactId>kubernetes-client</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>io.fabric8</groupId>
+ <artifactId>kubernetes-server-mock</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>3.4.6</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-junit-jupiter</artifactId>
+ <version>3.4.6</version>
+ <scope>test</scope>
+ </dependency>
+
+ </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistry.java b/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistry.java
new file mode 100644
index 0000000..b221c89
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistry.java
@@ -0,0 +1,58 @@
+/*
+ * 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.kubernetes;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.NotifyListener;
+import org.apache.dubbo.registry.support.FailbackRegistry;
+
+/**
+ * Empty implements for Kubernetes <br/>
+ * Kubernetes only support `Service Discovery` mode register <br/>
+ * Used to compat past version like 2.6.x, 2.7.x with interface level register <br/>
+ * {@link KubernetesServiceDiscovery} is the real implementation of Kubernetes
+ */
+public class KubernetesRegistry extends FailbackRegistry {
+ public KubernetesRegistry(URL url) {
+ super(url);
+ }
+
+ @Override
+ public boolean isAvailable() {
+ return true;
+ }
+
+ @Override
+ public void doRegister(URL url) {
+
+ }
+
+ @Override
+ public void doUnregister(URL url) {
+
+ }
+
+ @Override
+ public void doSubscribe(URL url, NotifyListener listener) {
+
+ }
+
+ @Override
+ public void doUnsubscribe(URL url, NotifyListener listener) {
+
+ }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java b/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistryFactory.java
similarity index 61%
copy from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
copy to dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistryFactory.java
index 06457f6..ab9af54 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
+++ b/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesRegistryFactory.java
@@ -14,19 +14,21 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.qos.command.impl;
+package org.apache.dubbo.registry.kubernetes;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.qos.command.BaseCommand;
-import org.apache.dubbo.qos.command.CommandContext;
-import org.apache.dubbo.qos.command.annotation.Cmd;
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.Registry;
+import org.apache.dubbo.registry.support.AbstractRegistryFactory;
-@Cmd(name = "start",summary = "Judge if service has started? ")
-public class Ready implements BaseCommand {
+public class KubernetesRegistryFactory extends AbstractRegistryFactory {
@Override
- public String execute(CommandContext commandContext, String[] args) {
- return DubboBootstrap.getInstance().isReady() ? "true" : "false";
+ protected String createRegistryCacheKey(URL url) {
+ return url.toFullString();
}
+ @Override
+ protected Registry createRegistry(URL url) {
+ return new KubernetesRegistry(url);
+ }
}
diff --git a/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java b/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java
new file mode 100644
index 0000000..f75837a
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscovery.java
@@ -0,0 +1,404 @@
+/*
+ * 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.kubernetes;
+
+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.StringUtils;
+import org.apache.dubbo.registry.client.DefaultServiceInstance;
+import org.apache.dubbo.registry.client.ServiceDiscovery;
+import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
+import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
+import org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst;
+import org.apache.dubbo.registry.kubernetes.util.KubernetesConfigUtils;
+
+import com.alibaba.fastjson.JSONObject;
+import io.fabric8.kubernetes.api.model.EndpointAddress;
+import io.fabric8.kubernetes.api.model.EndpointPort;
+import io.fabric8.kubernetes.api.model.EndpointSubset;
+import io.fabric8.kubernetes.api.model.Endpoints;
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.api.model.Service;
+import io.fabric8.kubernetes.client.Config;
+import io.fabric8.kubernetes.client.DefaultKubernetesClient;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.KubernetesClientException;
+import io.fabric8.kubernetes.client.Watch;
+import io.fabric8.kubernetes.client.Watcher;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.stream.Collectors;
+
+public class KubernetesServiceDiscovery implements ServiceDiscovery {
+ private final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private KubernetesClient kubernetesClient;
+
+ private String currentHostname;
+
+ private ServiceInstance localServiceInstance;
+
+ private URL registryURL;
+
+ private String namespace;
+
+ private boolean enableRegister;
+
+ public final static String KUBERNETES_PROPERTIES_KEY = "io.dubbo/metadata";
+
+ private final static ConcurrentHashMap<String, Watch> SERVICE_WATCHER = new ConcurrentHashMap<>(64);
+
+ private final static ConcurrentHashMap<String, Watch> PODS_WATCHER = new ConcurrentHashMap<>(64);
+
+ private final static ConcurrentHashMap<String, Watch> ENDPOINTS_WATCHER = new ConcurrentHashMap<>(64);
+
+ private final static ConcurrentHashMap<String, AtomicLong> SERVICE_UPDATE_TIME = new ConcurrentHashMap<>(64);
+
+ @Override
+ public void initialize(URL registryURL) throws Exception {
+ Config config = KubernetesConfigUtils.createKubernetesConfig(registryURL);
+ this.kubernetesClient = new DefaultKubernetesClient(config);
+ this.currentHostname = System.getenv("HOSTNAME");
+ this.registryURL = registryURL;
+ this.namespace = config.getNamespace();
+ this.enableRegister = registryURL.getParameter(KubernetesClientConst.ENABLE_REGISTER, true);
+
+ boolean availableAccess;
+ try {
+ availableAccess = kubernetesClient.pods().withName(currentHostname).get() != null;
+ } catch (Throwable e) {
+ availableAccess = false;
+ }
+ if (!availableAccess) {
+ String message = "Unable to access api server. " +
+ "Please check your url config." +
+ " Master URL: " + config.getMasterUrl() +
+ " Hostname: " + currentHostname;
+ logger.error(message);
+ }
+ }
+
+ @Override
+ public void destroy() throws Exception {
+ SERVICE_WATCHER.forEach((k, v) -> v.close());
+ SERVICE_WATCHER.clear();
+
+ PODS_WATCHER.forEach((k, v) -> v.close());
+ PODS_WATCHER.clear();
+
+ ENDPOINTS_WATCHER.forEach((k, v) -> v.close());
+ ENDPOINTS_WATCHER.clear();
+
+ kubernetesClient.close();
+ }
+
+ @Override
+ public void register(ServiceInstance serviceInstance) throws RuntimeException {
+ localServiceInstance = serviceInstance;
+
+ if (enableRegister) {
+ kubernetesClient
+ .pods()
+ .inNamespace(namespace)
+ .withName(currentHostname)
+ .edit()
+ .editOrNewMetadata()
+ .addToAnnotations(KUBERNETES_PROPERTIES_KEY, JSONObject.toJSONString(serviceInstance.getMetadata()))
+ .endMetadata()
+ .done();
+ if (logger.isInfoEnabled()) {
+ logger.info("Write Current Service Instance Metadata to Kubernetes pod. " +
+ "Current pod name: " + currentHostname);
+ }
+ }
+ }
+
+ @Override
+ public void update(ServiceInstance serviceInstance) throws RuntimeException {
+ register(serviceInstance);
+ }
+
+ @Override
+ public void unregister(ServiceInstance serviceInstance) throws RuntimeException {
+ localServiceInstance = null;
+
+ if (enableRegister) {
+ kubernetesClient
+ .pods()
+ .inNamespace(namespace)
+ .withName(currentHostname)
+ .edit()
+ .editOrNewMetadata()
+ .removeFromAnnotations(KUBERNETES_PROPERTIES_KEY)
+ .endMetadata()
+ .done();
+ if (logger.isInfoEnabled()) {
+ logger.info("Remove Current Service Instance from Kubernetes pod. Current pod name: " + currentHostname);
+ }
+ }
+ }
+
+ @Override
+ public Set<String> getServices() {
+ return kubernetesClient
+ .services()
+ .inNamespace(namespace)
+ .list()
+ .getItems()
+ .stream()
+ .map(service -> service.getMetadata().getName())
+ .collect(Collectors.toSet());
+ }
+
+ @Override
+ public ServiceInstance getLocalInstance() {
+ return localServiceInstance;
+ }
+
+ @Override
+ public List<ServiceInstance> getInstances(String serviceName) throws NullPointerException {
+ Endpoints endpoints =
+ kubernetesClient
+ .endpoints()
+ .inNamespace(namespace)
+ .withName(serviceName)
+ .get();
+
+ return toServiceInstance(endpoints, serviceName);
+ }
+
+ @Override
+ public void addServiceInstancesChangedListener(ServiceInstancesChangedListener listener) throws NullPointerException, IllegalArgumentException {
+ listener.getServiceNames().forEach(serviceName -> {
+ SERVICE_UPDATE_TIME.put(serviceName, new AtomicLong(0L));
+
+ // Watch Service Endpoint Modification
+ watchEndpoints(listener, serviceName);
+
+ // Watch Pods Modification, happens when ServiceInstance updated
+ watchPods(listener, serviceName);
+
+ // Watch Service Modification, happens when Service Selector updated, used to update pods watcher
+ watchService(listener, serviceName);
+ });
+ }
+
+ private void watchEndpoints(ServiceInstancesChangedListener listener, String serviceName) {
+ Watch watch = kubernetesClient
+ .endpoints()
+ .inNamespace(namespace)
+ .withName(serviceName)
+ .watch(new Watcher<Endpoints>() {
+ @Override
+ public void eventReceived(Action action, Endpoints resource) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Received Endpoint Event. Event type: " + action.name() +
+ ". Current pod name: " + currentHostname);
+ }
+
+ notifyServiceChanged(serviceName, listener);
+ }
+
+ @Override
+ public void onClose(KubernetesClientException cause) {
+ // ignore
+ }
+ });
+
+ ENDPOINTS_WATCHER.put(serviceName, watch);
+ }
+
+ private void watchPods(ServiceInstancesChangedListener listener, String serviceName) {
+ Map<String, String> serviceSelector = getServiceSelector(serviceName);
+ if (serviceSelector == null) {
+ return;
+ }
+
+ Watch watch = kubernetesClient
+ .pods()
+ .inNamespace(namespace)
+ .withLabels(serviceSelector)
+ .watch(new Watcher<Pod>() {
+ @Override
+ public void eventReceived(Action action, Pod resource) {
+ if (Action.MODIFIED.equals(action)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Received Pods Update Event. Current pod name: " + currentHostname);
+ }
+
+ notifyServiceChanged(serviceName, listener);
+ }
+ }
+
+ @Override
+ public void onClose(KubernetesClientException cause) {
+ // ignore
+ }
+ });
+
+ PODS_WATCHER.put(serviceName, watch);
+ }
+
+ private void watchService(ServiceInstancesChangedListener listener, String serviceName) {
+ Watch watch = kubernetesClient
+ .services()
+ .inNamespace(namespace)
+ .withName(serviceName)
+ .watch(new Watcher<Service>() {
+ @Override
+ public void eventReceived(Action action, Service resource) {
+ if (Action.MODIFIED.equals(action)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Received Service Update Event. Update Pods Watcher. " +
+ "Current pod name: " + currentHostname);
+ }
+
+ if (PODS_WATCHER.containsKey(serviceName)) {
+ PODS_WATCHER.get(serviceName).close();
+ PODS_WATCHER.remove(serviceName);
+ }
+ watchPods(listener, serviceName);
+ }
+ }
+
+ @Override
+ public void onClose(KubernetesClientException cause) {
+ // ignore
+ }
+ });
+
+ SERVICE_WATCHER.put(serviceName, watch);
+ }
+
+ private void notifyServiceChanged(String serviceName, ServiceInstancesChangedListener listener) {
+ long receivedTime = System.nanoTime();
+
+ ServiceInstancesChangedEvent event;
+
+ event = new ServiceInstancesChangedEvent(serviceName, getInstances(serviceName));
+
+ AtomicLong updateTime = SERVICE_UPDATE_TIME.get(serviceName);
+ long lastUpdateTime = updateTime.get();
+
+ if (lastUpdateTime <= receivedTime) {
+ if (updateTime.compareAndSet(lastUpdateTime, receivedTime)) {
+ listener.onEvent(event);
+ return;
+ }
+ }
+
+ if (logger.isInfoEnabled()) {
+ logger.info("Discard Service Instance Data. " +
+ "Possible Cause: Newer message has been processed or Failed to update time record by CAS. " +
+ "Current Data received time: " + receivedTime + ". " +
+ "Newer Data received time: " + lastUpdateTime + ".");
+ }
+ }
+
+ @Override
+ public URL getUrl() {
+ return registryURL;
+ }
+
+ private Map<String, String> getServiceSelector(String serviceName) {
+ Service service = kubernetesClient.services().inNamespace(namespace).withName(serviceName).get();
+ if (service == null) {
+ return null;
+ }
+ return service.getSpec().getSelector();
+ }
+
+ private List<ServiceInstance> toServiceInstance(Endpoints endpoints, String serviceName) {
+ Map<String, String> serviceSelector = getServiceSelector(serviceName);
+ if (serviceSelector == null) {
+ return new LinkedList<>();
+ }
+ Map<String, Pod> pods = kubernetesClient
+ .pods()
+ .inNamespace(namespace)
+ .withLabels(serviceSelector)
+ .list()
+ .getItems()
+ .stream()
+ .collect(
+ Collectors.toMap(
+ pod -> pod.getMetadata().getName(),
+ pod -> pod));
+
+ List<ServiceInstance> instances = new LinkedList<>();
+ Set<Integer> instancePorts = new HashSet<>();
+
+ for (EndpointSubset endpointSubset : endpoints.getSubsets()) {
+ instancePorts.addAll(
+ endpointSubset.getPorts()
+ .stream().map(EndpointPort::getPort)
+ .collect(Collectors.toSet()));
+ }
+
+ for (EndpointSubset endpointSubset : endpoints.getSubsets()) {
+ for (EndpointAddress address : endpointSubset.getAddresses()) {
+ Pod pod = pods.get(address.getTargetRef().getName());
+ String ip = address.getIp();
+ if (pod == null) {
+ logger.warn("Unable to match Kubernetes Endpoint address with Pod. " +
+ "EndpointAddress Hostname: " + address.getTargetRef().getName());
+ continue;
+ }
+
+ instancePorts.forEach(port -> {
+ ServiceInstance serviceInstance = new DefaultServiceInstance(serviceName, ip, port);
+
+ String properties = pod.getMetadata().getAnnotations().get(KUBERNETES_PROPERTIES_KEY);
+ if (StringUtils.isNotEmpty(properties)) {
+ serviceInstance.getMetadata().putAll(JSONObject.parseObject(properties, Map.class));
+ instances.add(serviceInstance);
+ } else {
+ logger.warn("Unable to find Service Instance metadata in Pod Annotations. " +
+ "Possibly cause: provider has not been initialized successfully. " +
+ "EndpointAddress Hostname: " + address.getTargetRef().getName());
+ }
+ });
+ }
+ }
+
+ return instances;
+ }
+
+ /**
+ * UT used only
+ */
+ @Deprecated
+ public void setCurrentHostname(String currentHostname) {
+ this.currentHostname = currentHostname;
+ }
+
+ /**
+ * UT used only
+ */
+ @Deprecated
+ public void setKubernetesClient(KubernetesClient kubernetesClient) {
+ this.kubernetesClient = kubernetesClient;
+ }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java b/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryFactory.java
similarity index 61%
copy from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
copy to dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryFactory.java
index 06457f6..88d50e2 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
+++ b/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryFactory.java
@@ -14,19 +14,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.qos.command.impl;
+package org.apache.dubbo.registry.kubernetes;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.qos.command.BaseCommand;
-import org.apache.dubbo.qos.command.CommandContext;
-import org.apache.dubbo.qos.command.annotation.Cmd;
-
-@Cmd(name = "start",summary = "Judge if service has started? ")
-public class Ready implements BaseCommand {
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.client.AbstractServiceDiscoveryFactory;
+import org.apache.dubbo.registry.client.ServiceDiscovery;
+public class KubernetesServiceDiscoveryFactory extends AbstractServiceDiscoveryFactory {
@Override
- public String execute(CommandContext commandContext, String[] args) {
- return DubboBootstrap.getInstance().isReady() ? "true" : "false";
+ protected ServiceDiscovery createDiscovery(URL registryURL) {
+ return new KubernetesServiceDiscovery();
}
-
}
diff --git a/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesClientConst.java b/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesClientConst.java
new file mode 100644
index 0000000..527d2fa
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesClientConst.java
@@ -0,0 +1,76 @@
+/*
+ * 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.kubernetes.util;
+
+public class KubernetesClientConst {
+
+ public final static String ENABLE_REGISTER = "enableRegister";
+
+ public final static String TRUST_CERTS = "trustCerts";
+
+ public final static String USE_HTTPS = "useHttps";
+
+ public static final String HTTP2_DISABLE = "http2Disable";
+
+ public final static String NAMESPACE = "namespace";
+
+ public final static String API_VERSION = "apiVersion";
+
+ public final static String CA_CERT_FILE = "caCertFile";
+
+ public final static String CA_CERT_DATA = "caCertData";
+
+ public final static String CLIENT_CERT_FILE = "clientCertFile";
+
+ public final static String CLIENT_CERT_DATA = "clientCertData";
+
+ public final static String CLIENT_KEY_FILE = "clientKeyFile";
+
+ public final static String CLIENT_KEY_DATA = "clientKeyData";
+
+ public final static String CLIENT_KEY_ALGO = "clientKeyAlgo";
+
+ public final static String CLIENT_KEY_PASSPHRASE = "clientKeyPassphrase";
+
+ public final static String OAUTH_TOKEN = "oauthToken";
+
+ public final static String USERNAME = "username";
+
+ public final static String PASSWORD = "password";
+
+ public final static String WATCH_RECONNECT_INTERVAL = "watchReconnectInterval";
+
+ public final static String WATCH_RECONNECT_LIMIT = "watchReconnectLimit";
+
+ public final static String CONNECTION_TIMEOUT = "connectionTimeout";
+
+ public final static String REQUEST_TIMEOUT = "requestTimeout";
+
+ public final static String ROLLING_TIMEOUT = "rollingTimeout";
+
+ public final static String LOGGING_INTERVAL = "loggingInterval";
+
+ public final static String HTTP_PROXY = "httpProxy";
+
+ public final static String HTTPS_PROXY = "httpsProxy";
+
+ public final static String PROXY_USERNAME = "proxyUsername";
+
+ public final static String PROXY_PASSWORD = "proxyPassword";
+
+ public final static String NO_PROXY = "noProxy";
+}
diff --git a/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesConfigUtils.java b/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesConfigUtils.java
new file mode 100644
index 0000000..83b7eb0
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-kubernetes/src/main/java/org/apache/dubbo/registry/kubernetes/util/KubernetesConfigUtils.java
@@ -0,0 +1,111 @@
+/*
+ * 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.kubernetes.util;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.common.utils.StringUtils;
+
+import io.fabric8.kubernetes.client.Config;
+import io.fabric8.kubernetes.client.ConfigBuilder;
+
+import java.util.Base64;
+
+public class KubernetesConfigUtils {
+
+ public static Config createKubernetesConfig(URL url) {
+ // Init default config
+ Config base = Config.autoConfigure(null);
+
+ // replace config with parameters if presents
+ return new ConfigBuilder(base)
+ .withMasterUrl(buildMasterUrl(url))
+ .withApiVersion(url.getParameter(KubernetesClientConst.API_VERSION,
+ base.getApiVersion()))
+ .withNamespace(url.getParameter(KubernetesClientConst.NAMESPACE,
+ base.getNamespace()))
+ .withUsername(url.getParameter(KubernetesClientConst.USERNAME,
+ base.getUsername()))
+ .withPassword(url.getParameter(KubernetesClientConst.PASSWORD,
+ base.getPassword()))
+
+ .withOauthToken(url.getParameter(KubernetesClientConst.OAUTH_TOKEN,
+ base.getOauthToken()))
+
+ .withCaCertFile(url.getParameter(KubernetesClientConst.CA_CERT_FILE,
+ base.getCaCertFile()))
+ .withCaCertData(url.getParameter(KubernetesClientConst.CA_CERT_DATA,
+ decodeBase64(base.getCaCertData())))
+
+ .withClientKeyFile(url.getParameter(KubernetesClientConst.CLIENT_KEY_FILE,
+ base.getClientKeyFile()))
+ .withClientKeyData(url.getParameter(KubernetesClientConst.CLIENT_KEY_DATA,
+ decodeBase64(base.getClientKeyData())))
+
+ .withClientCertFile(url.getParameter(KubernetesClientConst.CLIENT_CERT_FILE,
+ base.getClientCertFile()))
+ .withClientCertData(url.getParameter(KubernetesClientConst.CLIENT_CERT_DATA,
+ decodeBase64(base.getClientCertData())))
+
+ .withClientKeyAlgo(url.getParameter(KubernetesClientConst.CLIENT_KEY_ALGO,
+ base.getClientKeyAlgo()))
+ .withClientKeyPassphrase(url.getParameter(KubernetesClientConst.CLIENT_KEY_PASSPHRASE,
+ base.getClientKeyPassphrase()))
+
+ .withConnectionTimeout(url.getParameter(KubernetesClientConst.CONNECTION_TIMEOUT,
+ base.getConnectionTimeout()))
+ .withRequestTimeout(url.getParameter(KubernetesClientConst.REQUEST_TIMEOUT,
+ base.getRequestTimeout()))
+ .withRollingTimeout(url.getParameter(KubernetesClientConst.ROLLING_TIMEOUT,
+ base.getRollingTimeout()))
+
+ .withWatchReconnectInterval(url.getParameter(KubernetesClientConst.WATCH_RECONNECT_INTERVAL,
+ base.getWatchReconnectInterval()))
+ .withWatchReconnectLimit(url.getParameter(KubernetesClientConst.WATCH_RECONNECT_LIMIT,
+ base.getWatchReconnectLimit()))
+ .withLoggingInterval(url.getParameter(KubernetesClientConst.LOGGING_INTERVAL,
+ base.getLoggingInterval()))
+
+ .withTrustCerts(url.getParameter(KubernetesClientConst.TRUST_CERTS,
+ base.isTrustCerts()))
+ .withHttp2Disable(url.getParameter(KubernetesClientConst.HTTP2_DISABLE,
+ base.isTrustCerts()))
+
+ .withHttpProxy(url.getParameter(KubernetesClientConst.HTTP_PROXY,
+ base.getHttpProxy()))
+ .withHttpsProxy(url.getParameter(KubernetesClientConst.HTTPS_PROXY,
+ base.getHttpsProxy()))
+ .withProxyUsername(url.getParameter(KubernetesClientConst.PROXY_USERNAME,
+ base.getProxyUsername()))
+ .withProxyPassword(url.getParameter(KubernetesClientConst.PROXY_PASSWORD,
+ base.getProxyPassword()))
+ .withNoProxy(url.getParameter(KubernetesClientConst.NO_PROXY,
+ base.getNoProxy()))
+ .build();
+ }
+
+ private static String buildMasterUrl(URL url) {
+ return (url.getParameter(KubernetesClientConst.USE_HTTPS, true) ?
+ "https://" : "http://")
+ + url.getHost() + ":" + url.getPort();
+ }
+
+ private static String decodeBase64(String str) {
+ return StringUtils.isNotEmpty(str) ?
+ new String(Base64.getDecoder().decode(str)) :
+ "";
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory b/dubbo-registry/dubbo-registry-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
new file mode 100644
index 0000000..94177d8
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.RegistryFactory
@@ -0,0 +1 @@
+kubernetes=org.apache.dubbo.registry.kubernetes.KubernetesRegistryFactory
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery b/dubbo-registry/dubbo-registry-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
new file mode 100644
index 0000000..3e1b88e
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
@@ -0,0 +1 @@
+kubernetes=org.apache.dubbo.registry.kubernetes.KubernetesServiceDiscovery
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory b/dubbo-registry/dubbo-registry-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
new file mode 100644
index 0000000..4301ab8
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-kubernetes/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
@@ -0,0 +1 @@
+kubernetes=org.apache.dubbo.registry.kubernetes.KubernetesServiceDiscoveryFactory
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-kubernetes/src/test/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryTest.java b/dubbo-registry/dubbo-registry-kubernetes/src/test/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryTest.java
new file mode 100644
index 0000000..d3d8c9b
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-kubernetes/src/test/java/org/apache/dubbo/registry/kubernetes/KubernetesServiceDiscoveryTest.java
@@ -0,0 +1,191 @@
+/*
+ * 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.kubernetes;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.client.DefaultServiceInstance;
+import org.apache.dubbo.registry.client.ServiceInstance;
+import org.apache.dubbo.registry.client.event.ServiceInstancesChangedEvent;
+import org.apache.dubbo.registry.client.event.listener.ServiceInstancesChangedListener;
+import org.apache.dubbo.registry.kubernetes.util.KubernetesClientConst;
+
+import io.fabric8.kubernetes.api.model.Endpoints;
+import io.fabric8.kubernetes.api.model.EndpointsBuilder;
+import io.fabric8.kubernetes.api.model.Pod;
+import io.fabric8.kubernetes.api.model.PodBuilder;
+import io.fabric8.kubernetes.api.model.Service;
+import io.fabric8.kubernetes.api.model.ServiceBuilder;
+import io.fabric8.kubernetes.client.Config;
+import io.fabric8.kubernetes.client.KubernetesClient;
+import io.fabric8.kubernetes.client.server.mock.KubernetesServer;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+@ExtendWith({MockitoExtension.class})
+public class KubernetesServiceDiscoveryTest {
+ public KubernetesServer mockServer = new KubernetesServer(true, true);
+
+ private KubernetesClient mockClient;
+
+ private ServiceInstancesChangedListener mockListener = Mockito.mock(ServiceInstancesChangedListener.class);
+
+ private URL serverUrl;
+
+ private Map<String, String> selector;
+
+ @BeforeEach
+ public void setUp() {
+ mockServer.before();
+ mockClient = mockServer.getClient();
+
+ serverUrl = URL.valueOf(mockClient.getConfiguration().getMasterUrl())
+ .setProtocol("kubernetes")
+ .addParameter(KubernetesClientConst.TRUST_CERTS, "true")
+ .addParameter(KubernetesClientConst.HTTP2_DISABLE, "true");
+
+ System.setProperty(Config.KUBERNETES_AUTH_TRYKUBECONFIG_SYSTEM_PROPERTY, "false");
+ System.setProperty(Config.KUBERNETES_AUTH_TRYSERVICEACCOUNT_SYSTEM_PROPERTY, "false");
+
+ selector = new HashMap<>(4);
+ selector.put("l", "v");
+ Pod pod = new PodBuilder()
+ .withNewMetadata().withName("TestServer").withLabels(selector).endMetadata()
+ .build();
+
+ Service service = new ServiceBuilder()
+ .withNewMetadata().withName("TestService").endMetadata()
+ .withNewSpec().withSelector(selector).endSpec().build();
+
+ Endpoints endPoints = new EndpointsBuilder()
+ .withNewMetadata().withName("TestService").endMetadata()
+ .addNewSubset()
+ .addNewAddress().withIp("ip1")
+ .withNewTargetRef().withUid("uid1").withName("TestServer").endTargetRef().endAddress()
+ .addNewPort("Test", "Test", 12345, "TCP").endSubset()
+ .build();
+
+ mockClient.pods().create(pod);
+ mockClient.services().create(service);
+ mockClient.endpoints().create(endPoints);
+ }
+
+ @AfterEach
+ public void destroy() {
+ mockServer.after();
+ }
+
+ @Test
+ public void testEndpointsUpdate() throws Exception {
+
+ KubernetesServiceDiscovery serviceDiscovery = new KubernetesServiceDiscovery();
+ serviceDiscovery.initialize(serverUrl);
+
+ serviceDiscovery.setCurrentHostname("TestServer");
+ serviceDiscovery.setKubernetesClient(mockClient);
+
+ ServiceInstance serviceInstance = new DefaultServiceInstance("TestService", "Test", 12345);
+ serviceDiscovery.register(serviceInstance);
+
+ HashSet<String> serviceList = new HashSet<>(4);
+ serviceList.add("TestService");
+ Mockito.when(mockListener.getServiceNames()).thenReturn(serviceList);
+ Mockito.doNothing().when(mockListener).onEvent(Mockito.any());
+
+ serviceDiscovery.addServiceInstancesChangedListener(mockListener);
+ mockClient.endpoints().withName("TestService").edit().editFirstSubset()
+ .addNewAddress().withIp("ip2")
+ .withNewTargetRef().withUid("uid2").withName("TestServer").endTargetRef().endAddress()
+ .addNewPort("Test", "Test", 12345, "TCP").endSubset()
+ .done();
+
+ Thread.sleep(5000);
+ ArgumentCaptor<ServiceInstancesChangedEvent> eventArgumentCaptor =
+ ArgumentCaptor.forClass(ServiceInstancesChangedEvent.class);
+ Mockito.verify(mockListener).onEvent(eventArgumentCaptor.capture());
+ Assertions.assertEquals(2, eventArgumentCaptor.getValue().getServiceInstances().size());
+
+ serviceDiscovery.unregister(serviceInstance);
+
+ serviceDiscovery.destroy();
+ }
+
+ @Test
+ public void testPodsUpdate() throws Exception {
+
+ KubernetesServiceDiscovery serviceDiscovery = new KubernetesServiceDiscovery();
+ serviceDiscovery.initialize(serverUrl);
+
+ serviceDiscovery.setCurrentHostname("TestServer");
+ serviceDiscovery.setKubernetesClient(mockClient);
+
+ ServiceInstance serviceInstance = new DefaultServiceInstance("TestService", "Test", 12345);
+ serviceDiscovery.register(serviceInstance);
+
+ HashSet<String> serviceList = new HashSet<>(4);
+ serviceList.add("TestService");
+ Mockito.when(mockListener.getServiceNames()).thenReturn(serviceList);
+ Mockito.doNothing().when(mockListener).onEvent(Mockito.any());
+
+ serviceDiscovery.addServiceInstancesChangedListener(mockListener);
+
+ serviceInstance = new DefaultServiceInstance("TestService", "Test12345", 12345);
+ serviceDiscovery.update(serviceInstance);
+
+ Thread.sleep(5000);
+ ArgumentCaptor<ServiceInstancesChangedEvent> eventArgumentCaptor =
+ ArgumentCaptor.forClass(ServiceInstancesChangedEvent.class);
+ Mockito.verify(mockListener).onEvent(eventArgumentCaptor.capture());
+ Assertions.assertEquals(1, eventArgumentCaptor.getValue().getServiceInstances().size());
+
+ serviceDiscovery.unregister(serviceInstance);
+
+ serviceDiscovery.destroy();
+ }
+
+ @Test
+ public void testGetInstance() throws Exception {
+ KubernetesServiceDiscovery serviceDiscovery = new KubernetesServiceDiscovery();
+ serviceDiscovery.initialize(serverUrl);
+
+ serviceDiscovery.setCurrentHostname("TestServer");
+ serviceDiscovery.setKubernetesClient(mockClient);
+
+ ServiceInstance serviceInstance = new DefaultServiceInstance("TestService", "Test", 12345);
+ serviceDiscovery.register(serviceInstance);
+
+ serviceDiscovery.update(serviceInstance);
+
+ Assertions.assertEquals(1, serviceDiscovery.getServices().size());
+ Assertions.assertEquals(1, serviceDiscovery.getInstances("TestService").size());
+
+ Assertions.assertEquals(serviceInstance, serviceDiscovery.getLocalInstance());
+
+ serviceDiscovery.unregister(serviceInstance);
+
+ serviceDiscovery.destroy();
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-kubernetes/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/dubbo-registry/dubbo-registry-kubernetes/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
new file mode 100644
index 0000000..ca6ee9c
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-kubernetes/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker
@@ -0,0 +1 @@
+mock-maker-inline
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastServiceDiscovery.java b/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastServiceDiscovery.java
new file mode 100644
index 0000000..234fdcb
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastServiceDiscovery.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.apache.dubbo.registry.multicast;
+
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.client.ServiceDiscovery;
+import org.apache.dubbo.registry.client.ServiceInstance;
+
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * TODO: make multicast protocol support Service Discovery
+ */
+public class MulticastServiceDiscovery implements ServiceDiscovery {
+ private URL registryURL;
+ private ServiceInstance serviceInstance;
+
+ @Override
+ public void initialize(URL registryURL) throws Exception {
+ this.registryURL = registryURL;
+ }
+
+ @Override
+ public void destroy() throws Exception {
+
+ }
+
+ @Override
+ public void register(ServiceInstance serviceInstance) throws RuntimeException {
+ this.serviceInstance = serviceInstance;
+ }
+
+ @Override
+ public void update(ServiceInstance serviceInstance) throws RuntimeException {
+ this.serviceInstance = serviceInstance;
+ }
+
+ @Override
+ public void unregister(ServiceInstance serviceInstance) throws RuntimeException {
+ this.serviceInstance = null;
+ }
+
+ @Override
+ public Set<String> getServices() {
+ return Collections.singleton("Unsupported Operation");
+ }
+
+ @Override
+ public URL getUrl() {
+ return registryURL;
+ }
+
+ @Override
+ public ServiceInstance getLocalInstance() {
+ return serviceInstance;
+ }
+}
diff --git a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java b/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastServiceDiscoveryFactory.java
similarity index 61%
copy from dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
copy to dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastServiceDiscoveryFactory.java
index 06457f6..7bef1f5 100644
--- a/dubbo-plugin/dubbo-qos/src/main/java/org/apache/dubbo/qos/command/impl/Ready.java
+++ b/dubbo-registry/dubbo-registry-multicast/src/main/java/org/apache/dubbo/registry/multicast/MulticastServiceDiscoveryFactory.java
@@ -14,19 +14,15 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.dubbo.qos.command.impl;
+package org.apache.dubbo.registry.multicast;
-import org.apache.dubbo.config.bootstrap.DubboBootstrap;
-import org.apache.dubbo.qos.command.BaseCommand;
-import org.apache.dubbo.qos.command.CommandContext;
-import org.apache.dubbo.qos.command.annotation.Cmd;
-
-@Cmd(name = "start",summary = "Judge if service has started? ")
-public class Ready implements BaseCommand {
+import org.apache.dubbo.common.URL;
+import org.apache.dubbo.registry.client.ServiceDiscovery;
+import org.apache.dubbo.registry.client.ServiceDiscoveryFactory;
+public class MulticastServiceDiscoveryFactory implements ServiceDiscoveryFactory {
@Override
- public String execute(CommandContext commandContext, String[] args) {
- return DubboBootstrap.getInstance().isReady() ? "true" : "false";
+ public ServiceDiscovery getServiceDiscovery(URL registryURL) {
+ return new MulticastServiceDiscovery();
}
-
}
diff --git a/dubbo-registry/dubbo-registry-multicast/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery b/dubbo-registry/dubbo-registry-multicast/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
new file mode 100644
index 0000000..091b549
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-multicast/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscovery
@@ -0,0 +1 @@
+multicast=org.apache.dubbo.registry.multicast.MulticastServiceDiscovery
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-multicast/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory b/dubbo-registry/dubbo-registry-multicast/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
new file mode 100644
index 0000000..03eb6fa
--- /dev/null
+++ b/dubbo-registry/dubbo-registry-multicast/src/main/resources/META-INF/dubbo/internal/org.apache.dubbo.registry.client.ServiceDiscoveryFactory
@@ -0,0 +1 @@
+multicast=org.apache.dubbo.registry.multicast.MulticastServiceDiscoveryFactory
\ No newline at end of file
diff --git a/dubbo-registry/dubbo-registry-multicast/src/test/java/org/apache/dubbo/registry/multicast/MulticastRegistryTest.java b/dubbo-registry/dubbo-registry-multicast/src/test/java/org/apache/dubbo/registry/multicast/MulticastRegistryTest.java
index 76eece0..f0b9f1e 100644
--- a/dubbo-registry/dubbo-registry-multicast/src/test/java/org/apache/dubbo/registry/multicast/MulticastRegistryTest.java
+++ b/dubbo-registry/dubbo-registry-multicast/src/test/java/org/apache/dubbo/registry/multicast/MulticastRegistryTest.java
@@ -30,6 +30,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
+import static org.apache.dubbo.common.constants.RegistryConstants.EMPTY_PROTOCOL;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -39,7 +40,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
public class MulticastRegistryTest {
private String service = "org.apache.dubbo.test.injvmServie";
- private URL registryUrl = URL.valueOf("multicast://239.255.255.255/");
+ private URL registryUrl = URL.valueOf("multicast://239.239.239.239/");
private URL serviceUrl = URL.valueOf("dubbo://" + NetUtils.getLocalHost() + "/" + service
+ "?methods=test1,test2");
private URL adminUrl = URL.valueOf("dubbo://" + NetUtils.getLocalHost() + "/*");
@@ -79,7 +80,7 @@ public class MulticastRegistryTest {
@Test
public void testGetCustomPort() {
int port = NetUtils.getAvailablePort();
- URL customPortUrl = URL.valueOf("multicast://239.255.255.255:" + port);
+ URL customPortUrl = URL.valueOf("multicast://239.239.239.239:" + port);
MulticastRegistry multicastRegistry = new MulticastRegistry(customPortUrl);
assertThat(multicastRegistry.getUrl().getPort(), is(port));
}
@@ -132,15 +133,20 @@ public class MulticastRegistryTest {
@Test
public void testSubscribe() {
// verify listener
- registry.subscribe(consumerUrl, new NotifyListener() {
- @Override
- public void notify(List<URL> urls) {
- assertEquals(serviceUrl.toFullString(), urls.get(0).toFullString());
+ final URL[] notifyUrl = new URL[1];
+ for (int i = 0; i < 10; i++) {
+ registry.register(serviceUrl);
+ registry.subscribe(consumerUrl, urls -> {
+ notifyUrl[0] = urls.get(0);
Map<URL, Set<NotifyListener>> subscribed = registry.getSubscribed();
assertEquals(consumerUrl, subscribed.keySet().iterator().next());
+ });
+ if (!EMPTY_PROTOCOL.equalsIgnoreCase(notifyUrl[0].getProtocol())) {
+ break;
}
- });
+ }
+ assertEquals(serviceUrl.toFullString(), notifyUrl[0].toFullString());
}
/**
diff --git a/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java b/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java
index 0a30a0a..5f3b680 100644
--- a/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java
+++ b/dubbo-registry/dubbo-registry-zookeeper/src/test/java/org/apache/dubbo/registry/zookeeper/ZookeeperServiceDiscoveryTest.java
@@ -33,6 +33,7 @@ import java.util.Map;
import static java.util.Arrays.asList;
import static org.apache.dubbo.common.utils.NetUtils.getAvailablePort;
+import static org.apache.dubbo.registry.client.metadata.ServiceInstanceMetadataUtils.INSTANCE_REVISION_UPDATED_KEY;
import static org.apache.dubbo.registry.zookeeper.util.CuratorFrameworkUtils.generateId;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -73,7 +74,7 @@ public class ZookeeperServiceDiscoveryTest {
}
@Test
- public void testRegistration() {
+ public void testRegistration() throws InterruptedException {
DefaultServiceInstance serviceInstance = createServiceInstance(SERVICE_NAME, LOCALHOST, NetUtils.getAvailablePort());
@@ -87,6 +88,7 @@ public class ZookeeperServiceDiscoveryTest {
Map<String, String> metadata = new HashMap<>();
metadata.put("message", "Hello,World");
serviceInstance.setMetadata(metadata);
+ serviceInstance.getExtendParams().put(INSTANCE_REVISION_UPDATED_KEY, "true");
discovery.update(serviceInstance);
diff --git a/dubbo-registry/pom.xml b/dubbo-registry/pom.xml
index 3f9b247..db3ee35 100644
--- a/dubbo-registry/pom.xml
+++ b/dubbo-registry/pom.xml
@@ -35,5 +35,7 @@
<module>dubbo-registry-zookeeper</module>
<module>dubbo-registry-nacos</module>
<module>dubbo-registry-multiple</module>
+ <module>dubbo-registry-kubernetes</module>
+ <module>dubbo-registry-dns</module>
</modules>
</project>
diff --git a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandlerTest.java b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandlerTest.java
index 7e88e7c..eda4b60 100644
--- a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandlerTest.java
+++ b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/exchange/support/header/HeartbeatHandlerTest.java
@@ -20,6 +20,7 @@ package org.apache.dubbo.remoting.exchange.support.header;
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.NetUtils;
import org.apache.dubbo.remoting.Channel;
import org.apache.dubbo.remoting.Constants;
import org.apache.dubbo.remoting.RemotingException;
@@ -35,6 +36,7 @@ import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.CountDownLatch;
public class HeartbeatHandlerTest {
@@ -61,9 +63,13 @@ public class HeartbeatHandlerTest {
@Test
public void testServerHeartbeat() throws Exception {
- URL serverURL = URL.valueOf("header://localhost:55555?transporter=netty3");
- serverURL = serverURL.addParameter(Constants.HEARTBEAT_KEY, 1000);
- TestHeartbeatHandler handler = new TestHeartbeatHandler();
+ URL serverURL = URL.valueOf("telnet://localhost:" + NetUtils.getAvailablePort(56780))
+ .addParameter(Constants.EXCHANGER_KEY, HeaderExchanger.NAME)
+ .addParameter(Constants.TRANSPORTER_KEY, "netty3")
+ .addParameter(Constants.HEARTBEAT_KEY, 1000);
+ CountDownLatch connect = new CountDownLatch(1);
+ CountDownLatch disconnect = new CountDownLatch(1);
+ TestHeartbeatHandler handler = new TestHeartbeatHandler(connect, disconnect);
server = Exchangers.bind(serverURL, handler);
System.out.println("Server bind successfully");
@@ -75,21 +81,25 @@ public class HeartbeatHandlerTest {
serverURL = serverURL.addParameter(Constants.RECONNECT_KEY, false);
client = Exchangers.connect(serverURL);
- Thread.sleep(10000);
+ disconnect.await();
Assertions.assertTrue(handler.disconnectCount > 0);
System.out.println("disconnect count " + handler.disconnectCount);
}
@Test
public void testHeartbeat() throws Exception {
- URL serverURL = URL.valueOf("header://localhost:55556?transporter=netty3");
- serverURL = serverURL.addParameter(Constants.HEARTBEAT_KEY, 1000);
- TestHeartbeatHandler handler = new TestHeartbeatHandler();
+ URL serverURL = URL.valueOf("telnet://localhost:" + NetUtils.getAvailablePort(56785))
+ .addParameter(Constants.EXCHANGER_KEY, HeaderExchanger.NAME)
+ .addParameter(Constants.TRANSPORTER_KEY, "netty3")
+ .addParameter(Constants.HEARTBEAT_KEY, 1000);
+ CountDownLatch connect = new CountDownLatch(1);
+ CountDownLatch disconnect = new CountDownLatch(1);
+ TestHeartbeatHandler handler = new TestHeartbeatHandler(connect, disconnect);
server = Exchangers.bind(serverURL, handler);
System.out.println("Server bind successfully");
client = Exchangers.connect(serverURL);
- Thread.sleep(10000);
+ connect.await();
System.err.println("++++++++++++++ disconnect count " + handler.disconnectCount);
System.err.println("++++++++++++++ connect count " + handler.connectCount);
Assertions.assertEquals(0, handler.disconnectCount);
@@ -99,15 +109,19 @@ public class HeartbeatHandlerTest {
@Test
public void testClientHeartbeat() throws Exception {
FakeChannelHandlers.setTestingChannelHandlers();
- URL serverURL = URL.valueOf("header://localhost:55557?transporter=netty3");
- TestHeartbeatHandler handler = new TestHeartbeatHandler();
+ URL serverURL = URL.valueOf("telnet://localhost:" + NetUtils.getAvailablePort(56790))
+ .addParameter(Constants.EXCHANGER_KEY, HeaderExchanger.NAME)
+ .addParameter(Constants.TRANSPORTER_KEY, "netty3");
+ CountDownLatch connect = new CountDownLatch(1);
+ CountDownLatch disconnect = new CountDownLatch(1);
+ TestHeartbeatHandler handler = new TestHeartbeatHandler(connect, disconnect);
server = Exchangers.bind(serverURL, handler);
System.out.println("Server bind successfully");
FakeChannelHandlers.resetChannelHandlers();
serverURL = serverURL.addParameter(Constants.HEARTBEAT_KEY, 1000);
client = Exchangers.connect(serverURL);
- Thread.sleep(10000);
+ connect.await();
Assertions.assertTrue(handler.connectCount > 0);
System.out.println("connect count " + handler.connectCount);
}
@@ -116,6 +130,13 @@ public class HeartbeatHandlerTest {
public int disconnectCount = 0;
public int connectCount = 0;
+ private CountDownLatch connectCountDownLatch;
+ private CountDownLatch disconnectCountDownLatch;
+
+ public TestHeartbeatHandler(CountDownLatch connectCountDownLatch, CountDownLatch disconnectCountDownLatch) {
+ this.connectCountDownLatch = connectCountDownLatch;
+ this.disconnectCountDownLatch = disconnectCountDownLatch;
+ }
public CompletableFuture<Object> reply(ExchangeChannel channel, Object request) throws RemotingException {
return CompletableFuture.completedFuture(request);
@@ -124,11 +145,13 @@ public class HeartbeatHandlerTest {
@Override
public void connected(Channel channel) throws RemotingException {
++connectCount;
+ connectCountDownLatch.countDown();
}
@Override
public void disconnected(Channel channel) throws RemotingException {
++disconnectCount;
+ disconnectCountDownLatch.countDown();
}
@Override
diff --git a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ClientReconnectTest.java b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ClientReconnectTest.java
index 65fa6a0..a75a429 100644
--- a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ClientReconnectTest.java
+++ b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ClientReconnectTest.java
@@ -47,7 +47,7 @@ public class ClientReconnectTest {
Client client = startClient(port, 200);
Assertions.assertFalse(client.isConnected());
RemotingServer server = startServer(port);
- for (int i = 0; i < 100 && !client.isConnected(); i++) {
+ for (int i = 0; i < 1000 && !client.isConnected(); i++) {
Thread.sleep(10);
}
Assertions.assertTrue(client.isConnected());
@@ -70,7 +70,8 @@ public class ClientReconnectTest {
public Client startClient(int port, int heartbeat) throws RemotingException {
- final String url = "exchange://127.0.0.1:" + port + "/client.reconnect.test?check=false&client=netty3&" + Constants.HEARTBEAT_KEY + "=" + heartbeat;
+ final String url = "exchange://127.0.0.1:" + port + "/client.reconnect.test?check=false&client=netty3&" +
+ Constants.HEARTBEAT_KEY + "=" + heartbeat;
return Exchangers.connect(url);
}
diff --git a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ThreadNameTest.java b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ThreadNameTest.java
index 1228fe0..001b56a 100644
--- a/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ThreadNameTest.java
+++ b/dubbo-remoting/dubbo-remoting-netty/src/test/java/org/apache/dubbo/remoting/transport/netty/ThreadNameTest.java
@@ -44,8 +44,8 @@ public class ThreadNameTest {
@BeforeEach
public void before() throws Exception {
int port = NetUtils.getAvailablePort();
- serverURL = URL.valueOf("netty://localhost?side=provider").setPort(port);
- clientURL = URL.valueOf("netty://localhost?side=consumer").setPort(port);
+ serverURL = URL.valueOf("telnet://localhost?side=provider").setPort(port);
+ clientURL = URL.valueOf("telnet://localhost?side=consumer").setPort(port);
serverHandler = new ThreadNameVerifyHandler(serverRegex, false);
clientHandler = new ThreadNameVerifyHandler(clientRegex, true);
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyTransporterTest.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyTransporterTest.java
index 74e263e..0b13de1 100644
--- a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyTransporterTest.java
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/NettyTransporterTest.java
@@ -35,7 +35,7 @@ public class NettyTransporterTest {
@Test
public void shouldAbleToBindNetty4() throws Exception {
int port = NetUtils.getAvailablePort();
- URL url = new URL("http", "localhost", port,
+ URL url = new URL("telnet", "localhost", port,
new String[]{Constants.BIND_PORT_KEY, String.valueOf(port)});
RemotingServer server = new NettyTransporter().bind(url, new ChannelHandlerAdapter());
@@ -48,7 +48,7 @@ public class NettyTransporterTest {
final CountDownLatch lock = new CountDownLatch(1);
int port = NetUtils.getAvailablePort();
- URL url = new URL("http", "localhost", port,
+ URL url = new URL("telnet", "localhost", port,
new String[]{Constants.BIND_PORT_KEY, String.valueOf(port)});
new NettyTransporter().bind(url, new ChannelHandlerAdapter() {
diff --git a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ReplierDispatcherTest.java b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ReplierDispatcherTest.java
index 3ed2aca..37940e6 100644
--- a/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ReplierDispatcherTest.java
+++ b/dubbo-remoting/dubbo-remoting-netty4/src/test/java/org/apache/dubbo/remoting/transport/netty4/ReplierDispatcherTest.java
@@ -24,6 +24,7 @@ import org.apache.dubbo.remoting.exchange.ExchangeChannel;
import org.apache.dubbo.remoting.exchange.ExchangeServer;
import org.apache.dubbo.remoting.exchange.Exchangers;
import org.apache.dubbo.remoting.exchange.support.ReplierDispatcher;
+
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
@@ -31,7 +32,11 @@ import org.junit.jupiter.api.Test;
import java.io.Serializable;
import java.util.Random;
-import java.util.concurrent.*;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
import static org.junit.jupiter.api.Assertions.fail;
@@ -54,13 +59,13 @@ public class ReplierDispatcherTest {
ReplierDispatcher dispatcher = new ReplierDispatcher();
dispatcher.addReplier(RpcMessage.class, new RpcMessageHandler());
dispatcher.addReplier(Data.class, (channel, msg) -> new StringMessage("hello world"));
- exchangeServer = Exchangers.bind(URL.valueOf("dubbo://localhost:" + port), dispatcher);
+ exchangeServer = Exchangers.bind(URL.valueOf("exchange://localhost:" + port + "?" + CommonConstants.TIMEOUT_KEY + "=60000"), dispatcher);
}
@Test
public void testDataPackage() throws Exception {
- ExchangeChannel client = Exchangers.connect(URL.valueOf("dubbo://localhost:" + port));
+ ExchangeChannel client = Exchangers.connect(URL.valueOf("exchange://localhost:" + port + "?" + CommonConstants.TIMEOUT_KEY + "=60000"));
Random random = new Random();
for (int i = 5; i < 100; i++) {
StringBuilder sb = new StringBuilder();
@@ -91,7 +96,7 @@ public class ReplierDispatcherTest {
}
void clientExchangeInfo(int port) throws Exception {
- ExchangeChannel client = Exchangers.connect(URL.valueOf("dubbo://localhost:" + port + "?" + CommonConstants.TIMEOUT_KEY + "=5000"));
+ ExchangeChannel client = Exchangers.connect(URL.valueOf("exchange://localhost:" + port + "?" + CommonConstants.TIMEOUT_KEY + "=5000"));
clients.put(Thread.currentThread().getName(), client);
MockResult result = (MockResult) client.request(new RpcMessage(DemoService.class.getName(), "plus", new Class<?>[]{int.class, int.class}, new Object[]{55, 25})).get();
Assertions.assertEquals(result.getResult(), 80);
diff --git a/pom.xml b/pom.xml
index e2c22aa..0fdcc22 100644
--- a/pom.xml
+++ b/pom.xml
@@ -581,6 +581,8 @@
</exclude>
<exclude>.github/**</exclude>
<exclude>compiler/**</exclude>
+ <!-- exclude mockito extensions spi files -->
+ <exclude>**/mockito-extensions/*</exclude>
</excludes>
</configuration>
</execution>