diff --git a/all/pom.xml b/all/pom.xml
index 09bfe85daf8..edc0c4ad7a1 100644
--- a/all/pom.xml
+++ b/all/pom.xml
@@ -319,6 +319,13 @@
compile
true
+
+ com.alibaba
+ dubbo-bootstrap
+ ${project.version}
+ compile
+ true
+
com.alibaba
hessian-lite
@@ -418,6 +425,7 @@
com.alibaba:dubbo-serialization-fst
com.alibaba:dubbo-serialization-kryo
com.alibaba:dubbo-serialization-jdk
+ com.alibaba:dubbo-bootstrap
diff --git a/dubbo-bootstrap/pom.xml b/dubbo-bootstrap/pom.xml
new file mode 100644
index 00000000000..bb2b1a4ecd3
--- /dev/null
+++ b/dubbo-bootstrap/pom.xml
@@ -0,0 +1,47 @@
+
+
+
+ dubbo-parent
+ com.alibaba
+ 2.6.2-SNAPSHOT
+
+ 4.0.0
+
+ dubbo-bootstrap
+
+
+
+
+ com.alibaba
+ dubbo-config-api
+ ${project.parent.version}
+
+
+ com.alibaba
+ dubbo-common
+ ${project.parent.version}
+
+
+ com.alibaba
+ dubbo-registry-api
+ ${project.parent.version}
+
+
+
\ No newline at end of file
diff --git a/dubbo-bootstrap/src/main/java/org/apache/dubbo/bootstrap/DubboBootstrap.java b/dubbo-bootstrap/src/main/java/org/apache/dubbo/bootstrap/DubboBootstrap.java
new file mode 100644
index 00000000000..8694e48de35
--- /dev/null
+++ b/dubbo-bootstrap/src/main/java/org/apache/dubbo/bootstrap/DubboBootstrap.java
@@ -0,0 +1,133 @@
+/*
+ * 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.bootstrap;
+
+import com.alibaba.dubbo.common.extension.ExtensionLoader;
+import com.alibaba.dubbo.common.logger.Logger;
+import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.config.ServiceConfig;
+import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
+import com.alibaba.dubbo.rpc.Protocol;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * A bootstrap class to easily start and stop Dubbo via programmatic API.
+ * The bootstrap class will be responsible to cleanup the resources during stop.
+ */
+public class DubboBootstrap {
+
+ private static final Logger logger = LoggerFactory.getLogger(DubboBootstrap.class);
+
+ /**
+ * The list of ServiceConfig
+ */
+ private List serviceConfigList;
+
+ /**
+ * Has it already been destroyed or not?
+ */
+ private final AtomicBoolean destroyed;
+
+ /**
+ * The shutdown hook used when Dubbo is running under embedded environment
+ */
+ private Thread shutdownHook;
+
+ public DubboBootstrap() {
+ this.serviceConfigList = new ArrayList();
+ this.destroyed = new AtomicBoolean(false);
+ this.shutdownHook = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ if (logger.isInfoEnabled()) {
+ logger.info("Run shutdown hook now.");
+ }
+ destroy();
+ }
+ }, "DubboShutdownHook");
+ }
+
+ /**
+ * Register service config to bootstrap, which will be called during {@link DubboBootstrap#stop()}
+ * @param serviceConfig the service
+ * @return the bootstrap instance
+ */
+ public DubboBootstrap regsiterServiceConfig(ServiceConfig serviceConfig) {
+ serviceConfigList.add(serviceConfig);
+ return this;
+ }
+
+ public void start() {
+ registerShutdownHook();
+ for (ServiceConfig serviceConfig: serviceConfigList) {
+ serviceConfig.export();
+ }
+ }
+
+ public void stop() {
+ for (ServiceConfig serviceConfig: serviceConfigList) {
+ serviceConfig.unexport();
+ }
+ destroy();
+ removeShutdownHook();
+ }
+
+ /**
+ * Register the shutdown hook
+ */
+ public void registerShutdownHook() {
+ Runtime.getRuntime().addShutdownHook(shutdownHook);
+ }
+
+ /**
+ * Remove this shutdown hook
+ */
+ public void removeShutdownHook() {
+ try {
+ Runtime.getRuntime().removeShutdownHook(shutdownHook);
+ }
+ catch (IllegalStateException ex) {
+ // ignore - VM is already shutting down
+ }
+ }
+
+ /**
+ * Destroy all the resources, including registries and protocols.
+ */
+ private void destroy() {
+ if (!destroyed.compareAndSet(false, true)) {
+ return;
+ }
+ // destroy all the registries
+ AbstractRegistryFactory.destroyAll();
+ // destroy all the protocols
+ ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Protocol.class);
+ for (String protocolName : loader.getLoadedExtensions()) {
+ try {
+ Protocol protocol = loader.getLoadedExtension(protocolName);
+ if (protocol != null) {
+ protocol.destroy();
+ }
+ } catch (Throwable t) {
+ logger.warn(t.getMessage(), t);
+ }
+ }
+ }
+}
diff --git a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ExecutorUtil.java b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ExecutorUtil.java
index 6b07cc8035f..d605e130660 100644
--- a/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ExecutorUtil.java
+++ b/dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ExecutorUtil.java
@@ -43,19 +43,27 @@ public static boolean isTerminated(Executor executor) {
return false;
}
+ /**
+ * Use the shutdown pattern from:
+ * https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html
+ * @param executor the Executor to shutdown
+ * @param timeout the timeout in milliseconds before termination
+ */
public static void gracefulShutdown(Executor executor, int timeout) {
if (!(executor instanceof ExecutorService) || isTerminated(executor)) {
return;
}
final ExecutorService es = (ExecutorService) executor;
try {
- es.shutdown(); // Disable new tasks from being submitted
+ // Disable new tasks from being submitted
+ es.shutdown();
} catch (SecurityException ex2) {
return;
} catch (NullPointerException ex2) {
return;
}
try {
+ // Wait a while for existing tasks to terminate
if (!es.awaitTermination(timeout, TimeUnit.MILLISECONDS)) {
es.shutdownNow();
}
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java
index 73fc07da473..8d8d1828832 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java
@@ -73,18 +73,6 @@ public abstract class AbstractConfig implements Serializable {
legacyProperties.put("dubbo.service.url", "dubbo.service.address");
}
- static {
- Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
- @Override
- public void run() {
- if (logger.isInfoEnabled()) {
- logger.info("Run shutdown hook now.");
- }
- ProtocolConfig.destroyAll();
- }
- }, "DubboShutdownHook"));
- }
-
protected String id;
private static String convertLegacyValue(String key, String value) {
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java
index 0e7a52431c5..b15129bf6fb 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java
@@ -21,7 +21,6 @@
import com.alibaba.dubbo.common.status.StatusChecker;
import com.alibaba.dubbo.common.threadpool.ThreadPool;
import com.alibaba.dubbo.config.support.Parameter;
-import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
import com.alibaba.dubbo.remoting.Codec;
import com.alibaba.dubbo.remoting.Dispatcher;
import com.alibaba.dubbo.remoting.Transporter;
@@ -30,7 +29,6 @@
import com.alibaba.dubbo.rpc.Protocol;
import java.util.Map;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* ProtocolConfig
@@ -135,8 +133,6 @@ public class ProtocolConfig extends AbstractConfig {
// if it's default
private Boolean isDefault;
- private static final AtomicBoolean destroyed = new AtomicBoolean(false);
-
public ProtocolConfig() {
}
@@ -149,27 +145,6 @@ public ProtocolConfig(String name, int port) {
setPort(port);
}
- // TODO: 2017/8/30 to move this method somewhere else
- public static void destroyAll() {
- if (!destroyed.compareAndSet(false, true)) {
- return;
- }
-
- AbstractRegistryFactory.destroyAll();
-
- ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Protocol.class);
- for (String protocolName : loader.getLoadedExtensions()) {
- try {
- Protocol protocol = loader.getLoadedExtension(protocolName);
- if (protocol != null) {
- protocol.destroy();
- }
- } catch (Throwable t) {
- logger.warn(t.getMessage(), t);
- }
- }
- }
-
@Parameter(excluded = true)
public String getName() {
return name;
diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/RegistryConfig.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/RegistryConfig.java
index 7e2672b9c30..e5684f38be0 100644
--- a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/RegistryConfig.java
+++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/RegistryConfig.java
@@ -18,7 +18,6 @@
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.config.support.Parameter;
-import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
import java.util.Map;
@@ -96,13 +95,9 @@ public RegistryConfig(String address) {
setAddress(address);
}
- public static void destroyAll() {
- AbstractRegistryFactory.destroyAll();
- }
-
- @Deprecated
- public static void closeAll() {
- destroyAll();
+ public RegistryConfig(String address, String protocol) {
+ setAddress(address);
+ setProtocol(protocol);
}
public String getProtocol() {
diff --git a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/ProtocolConfigTest.java b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/ProtocolConfigTest.java
index 43f3621e287..fb105dd5d4e 100644
--- a/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/ProtocolConfigTest.java
+++ b/dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/ProtocolConfigTest.java
@@ -17,7 +17,6 @@
package com.alibaba.dubbo.config;
-import com.alibaba.dubbo.common.extension.ExtensionLoader;
import com.alibaba.dubbo.rpc.Protocol;
import org.junit.Test;
import org.mockito.Mockito;
@@ -32,15 +31,6 @@
import static org.junit.Assert.assertThat;
public class ProtocolConfigTest {
- @Test
- public void testDestroyAll() throws Exception {
- Protocol protocol = Mockito.mock(Protocol.class);
- MockProtocol2.delegate = protocol;
- ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Protocol.class);
- loader.getExtension("mockprotocol2");
- ProtocolConfig.destroyAll();
- Mockito.verify(protocol).destroy();
- }
@Test
public void testDestroy() throws Exception {
diff --git a/dubbo-config/dubbo-config-spring/pom.xml b/dubbo-config/dubbo-config-spring/pom.xml
index c7a666080b3..170ac2c1e85 100644
--- a/dubbo-config/dubbo-config-spring/pom.xml
+++ b/dubbo-config/dubbo-config-spring/pom.xml
@@ -35,14 +35,28 @@
dubbo-config-api
${project.parent.version}
+
+ com.alibaba
+ dubbo-bootstrap
+ ${project.parent.version}
+
org.springframework
spring-beans
+
+ org.springframework
+ spring-web
+
org.springframework
spring-context
+
+ javax.servlet
+ javax.servlet-api
+ provided
+
com.alibaba
dubbo-registry-default
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/initializer/DubboApplicationListener.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/initializer/DubboApplicationListener.java
new file mode 100644
index 00000000000..43ee49ded8c
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/initializer/DubboApplicationListener.java
@@ -0,0 +1,45 @@
+/*
+ * 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 com.alibaba.dubbo.config.spring.initializer;
+
+import org.apache.dubbo.bootstrap.DubboBootstrap;
+import org.springframework.context.ApplicationEvent;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.event.ContextClosedEvent;
+import org.springframework.context.event.ContextRefreshedEvent;
+
+/**
+ * An application listener that listens the ContextClosedEvent.
+ * Upon the event, this listener will do the necessary clean up to avoid memory leak.
+ */
+public class DubboApplicationListener implements ApplicationListener {
+
+ private DubboBootstrap dubboBootstrap;
+
+ public DubboApplicationListener() {
+ dubboBootstrap = new DubboBootstrap();
+ }
+
+ @Override
+ public void onApplicationEvent(ApplicationEvent applicationEvent) {
+ if (applicationEvent instanceof ContextRefreshedEvent) {
+ dubboBootstrap.start();
+ } else if (applicationEvent instanceof ContextClosedEvent) {
+ dubboBootstrap.stop();
+ }
+ }
+}
diff --git a/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/initializer/DubboWebApplicationInitializer.java b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/initializer/DubboWebApplicationInitializer.java
new file mode 100644
index 00000000000..8d0f79db14a
--- /dev/null
+++ b/dubbo-config/dubbo-config-spring/src/main/java/com/alibaba/dubbo/config/spring/initializer/DubboWebApplicationInitializer.java
@@ -0,0 +1,40 @@
+/*
+ * 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 com.alibaba.dubbo.config.spring.initializer;
+
+import org.springframework.web.context.AbstractContextLoaderInitializer;
+import org.springframework.web.context.WebApplicationContext;
+import org.springframework.web.context.support.XmlWebApplicationContext;
+
+/**
+ * An initializer to register {@link DubboApplicationListener}
+ * to the ApplicationContext seamlessly.
+ */
+public class DubboWebApplicationInitializer extends AbstractContextLoaderInitializer {
+
+ /**
+ * This method won't be triggered if running on spring-boot.
+ * It only works when running under a servlet container.
+ * @return a WebApplicationContext with DubboApplicationListener registered.
+ */
+ @Override
+ protected WebApplicationContext createRootApplicationContext() {
+ XmlWebApplicationContext webApplicationContext = new XmlWebApplicationContext();
+ webApplicationContext.addApplicationListener(new DubboApplicationListener());
+ return webApplicationContext;
+ }
+}
diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java
index ced0efad38d..e081957e681 100644
--- a/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java
+++ b/dubbo-registry/dubbo-registry-api/src/main/java/com/alibaba/dubbo/registry/support/FailbackRegistry.java
@@ -19,6 +19,7 @@
import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.common.utils.ExecutorUtil;
import com.alibaba.dubbo.common.utils.NamedThreadFactory;
import com.alibaba.dubbo.registry.NotifyListener;
@@ -57,9 +58,14 @@ public abstract class FailbackRegistry extends AbstractRegistry {
private final ConcurrentMap>> failedNotified = new ConcurrentHashMap>>();
+ /**
+ * The time in milliseconds the retryExecutor will wait
+ */
+ private final int retryPeriod;
+
public FailbackRegistry(URL url) {
super(url);
- int retryPeriod = url.getParameter(Constants.REGISTRY_RETRY_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RETRY_PERIOD);
+ this.retryPeriod = url.getParameter(Constants.REGISTRY_RETRY_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RETRY_PERIOD);
this.retryFuture = retryExecutor.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
@@ -440,6 +446,7 @@ public void destroy() {
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
+ ExecutorUtil.gracefulShutdown(retryExecutor, retryPeriod);
}
// ==== Template method ====
diff --git a/dubbo-registry/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistry.java b/dubbo-registry/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistry.java
index 9d5ab70159b..bf8056e6271 100644
--- a/dubbo-registry/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistry.java
+++ b/dubbo-registry/dubbo-registry-default/src/main/java/com/alibaba/dubbo/registry/dubbo/DubboRegistry.java
@@ -21,6 +21,7 @@
import com.alibaba.dubbo.common.Version;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ExecutorUtil;
import com.alibaba.dubbo.common.utils.NamedThreadFactory;
import com.alibaba.dubbo.common.utils.NetUtils;
import com.alibaba.dubbo.registry.NotifyListener;
@@ -47,7 +48,7 @@ public class DubboRegistry extends FailbackRegistry {
private static final int RECONNECT_PERIOD_DEFAULT = 3 * 1000;
// Scheduled executor service
- private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboRegistryReconnectTimer", true));
+ private final ScheduledExecutorService reconnectTimer = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboRegistryReconnectTimer", true));
// Reconnection timer, regular check connection is available. If unavailable, unlimited reconnection.
private final ScheduledFuture> reconnectFuture;
@@ -59,13 +60,18 @@ public class DubboRegistry extends FailbackRegistry {
private final RegistryService registryService;
+ /**
+ * The time in milliseconds the reconnectTimer will wait
+ */
+ private final int reconnectPeriod;
+
public DubboRegistry(Invoker registryInvoker, RegistryService registryService) {
super(registryInvoker.getUrl());
this.registryInvoker = registryInvoker;
this.registryService = registryService;
// Start reconnection timer
- int reconnectPeriod = registryInvoker.getUrl().getParameter(Constants.REGISTRY_RECONNECT_PERIOD_KEY, RECONNECT_PERIOD_DEFAULT);
- reconnectFuture = scheduledExecutorService.scheduleWithFixedDelay(new Runnable() {
+ this.reconnectPeriod = registryInvoker.getUrl().getParameter(Constants.REGISTRY_RECONNECT_PERIOD_KEY, RECONNECT_PERIOD_DEFAULT);
+ reconnectFuture = reconnectTimer.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
// Check and connect to the registry
@@ -127,6 +133,7 @@ public void destroy() {
logger.warn("Failed to cancel reconnect timer", t);
}
registryInvoker.destroy();
+ ExecutorUtil.gracefulShutdown(reconnectTimer, reconnectPeriod);
}
@Override
diff --git a/dubbo-registry/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistry.java b/dubbo-registry/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistry.java
index aa499fc1bc2..d3928397b3b 100644
--- a/dubbo-registry/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistry.java
+++ b/dubbo-registry/dubbo-registry-multicast/src/main/java/com/alibaba/dubbo/registry/multicast/MulticastRegistry.java
@@ -21,6 +21,7 @@
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
import com.alibaba.dubbo.common.utils.ConcurrentHashSet;
+import com.alibaba.dubbo.common.utils.ExecutorUtil;
import com.alibaba.dubbo.common.utils.NamedThreadFactory;
import com.alibaba.dubbo.common.utils.NetUtils;
import com.alibaba.dubbo.common.utils.StringUtils;
@@ -314,6 +315,7 @@ public void destroy() {
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
+ ExecutorUtil.gracefulShutdown(cleanExecutor, cleanPeriod);
}
protected void registered(URL url) {
diff --git a/dubbo-registry/dubbo-registry-redis/src/main/java/com/alibaba/dubbo/registry/redis/RedisRegistry.java b/dubbo-registry/dubbo-registry-redis/src/main/java/com/alibaba/dubbo/registry/redis/RedisRegistry.java
index ea4b22cba05..6a997918abf 100644
--- a/dubbo-registry/dubbo-registry-redis/src/main/java/com/alibaba/dubbo/registry/redis/RedisRegistry.java
+++ b/dubbo-registry/dubbo-registry-redis/src/main/java/com/alibaba/dubbo/registry/redis/RedisRegistry.java
@@ -20,6 +20,7 @@
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.logger.Logger;
import com.alibaba.dubbo.common.logger.LoggerFactory;
+import com.alibaba.dubbo.common.utils.ExecutorUtil;
import com.alibaba.dubbo.common.utils.NamedThreadFactory;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.common.utils.UrlUtils;
@@ -263,6 +264,7 @@ public void destroy() {
logger.warn("Failed to destroy the redis registry client. registry: " + entry.getKey() + ", cause: " + t.getMessage(), t);
}
}
+ ExecutorUtil.gracefulShutdown(expireExecutor, expirePeriod);
}
@Override
diff --git a/pom.xml b/pom.xml
index 856eca68ef7..9af7c4c947a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -133,6 +133,7 @@
dubbo-demo
dubbo-plugin
dubbo-serialization
+ dubbo-bootstrap
dependencies-bom
bom
all