From 1f0702b176e1d6a327c6488241242e613e323e21 Mon Sep 17 00:00:00 2001 From: Huxing Zhang Date: Sun, 20 May 2018 14:40:23 +0800 Subject: [PATCH 1/5] Improve compatibility for graceful shutdown. --- .../dubbo/bootstrap/DubboBootstrap.java | 54 ++---------- .../alibaba/dubbo/config/AbstractConfig.java | 2 + .../dubbo/config/DubboShutdownHook.java | 85 +++++++++++++++++++ .../alibaba/dubbo/config/ProtocolConfig.java | 9 ++ .../dubbo-container-spring/pom.xml | 5 ++ .../container/spring/SpringContainer.java | 2 + 6 files changed, 108 insertions(+), 49 deletions(-) create mode 100644 dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/DubboShutdownHook.java 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 index 8694e48de35..7f65ef3b6aa 100644 --- a/dubbo-bootstrap/src/main/java/org/apache/dubbo/bootstrap/DubboBootstrap.java +++ b/dubbo-bootstrap/src/main/java/org/apache/dubbo/bootstrap/DubboBootstrap.java @@ -16,16 +16,11 @@ */ 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.DubboShutdownHook; 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. @@ -33,35 +28,19 @@ */ 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; + private DubboShutdownHook 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"); + this.shutdownHook = DubboShutdownHook.getDubboShutdownHook(); } /** @@ -69,7 +48,7 @@ public void run() { * @param serviceConfig the service * @return the bootstrap instance */ - public DubboBootstrap regsiterServiceConfig(ServiceConfig serviceConfig) { + public DubboBootstrap registerServiceConfig(ServiceConfig serviceConfig) { serviceConfigList.add(serviceConfig); return this; } @@ -85,7 +64,7 @@ public void stop() { for (ServiceConfig serviceConfig: serviceConfigList) { serviceConfig.unexport(); } - destroy(); + shutdownHook.destroyAll(); removeShutdownHook(); } @@ -107,27 +86,4 @@ public void removeShutdownHook() { // 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-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 84ad29e7bbe..189877a6143 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 @@ -71,6 +71,8 @@ public abstract class AbstractConfig implements Serializable { legacyProperties.put("dubbo.consumer.retries", "dubbo.service.max.retry.providers"); legacyProperties.put("dubbo.consumer.check", "dubbo.service.allow.no.provider"); legacyProperties.put("dubbo.service.url", "dubbo.service.address"); + + Runtime.getRuntime().addShutdownHook(DubboShutdownHook.getDubboShutdownHook()); } protected String id; diff --git a/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/DubboShutdownHook.java b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/DubboShutdownHook.java new file mode 100644 index 00000000000..348c5e61035 --- /dev/null +++ b/dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/DubboShutdownHook.java @@ -0,0 +1,85 @@ +/* + * 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; + +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.registry.support.AbstractRegistryFactory; +import com.alibaba.dubbo.rpc.Protocol; + +import java.util.concurrent.atomic.AtomicBoolean; + +/** + * The shutdown hook thread to do the clean up stuff. + * This is a singleton in order to ensure there is only one shutdown hook registered. + * Because {@link ApplicationShutdownHooks} use {@link java.util.IdentityHashMap} + * to store the shutdown hooks. + */ +public class DubboShutdownHook extends Thread { + + private static final Logger logger = LoggerFactory.getLogger(DubboShutdownHook.class); + + private static final DubboShutdownHook dubboShutdownHook = new DubboShutdownHook("DubboShutdownHook"); + + public static DubboShutdownHook getDubboShutdownHook() { + return dubboShutdownHook; + } + + /** + * Has it already been destroyed or not? + */ + private final AtomicBoolean destroyed; + + private DubboShutdownHook(String name) { + super(name); + this.destroyed = new AtomicBoolean(false); + } + + @Override + public void run() { + if (logger.isInfoEnabled()) { + logger.info("Run shutdown hook now."); + } + destroyAll(); + } + + /** + * Destroy all the resources, including registries and protocols. + */ + public void destroyAll() { + 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-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 b15129bf6fb..b05d43113ec 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 @@ -460,4 +460,13 @@ public void destory() { } } + /** + * This method will do nothing. + * It should be deleted in the next major version, say 2.7.x. + * Just for compatibility. + */ + @Deprecated + public static void destroyAll() { + // DO NOTHING + } } \ No newline at end of file diff --git a/dubbo-container/dubbo-container-spring/pom.xml b/dubbo-container/dubbo-container-spring/pom.xml index 8221adf59af..fc385dcaed9 100644 --- a/dubbo-container/dubbo-container-spring/pom.xml +++ b/dubbo-container/dubbo-container-spring/pom.xml @@ -39,5 +39,10 @@ org.springframework spring-context + + com.alibaba + dubbo-config-spring + ${project.parent.version} + \ No newline at end of file diff --git a/dubbo-container/dubbo-container-spring/src/main/java/com/alibaba/dubbo/container/spring/SpringContainer.java b/dubbo-container/dubbo-container-spring/src/main/java/com/alibaba/dubbo/container/spring/SpringContainer.java index d21f3a5636f..05b5baec7b2 100644 --- a/dubbo-container/dubbo-container-spring/src/main/java/com/alibaba/dubbo/container/spring/SpringContainer.java +++ b/dubbo-container/dubbo-container-spring/src/main/java/com/alibaba/dubbo/container/spring/SpringContainer.java @@ -19,6 +19,7 @@ import com.alibaba.dubbo.common.logger.Logger; import com.alibaba.dubbo.common.logger.LoggerFactory; import com.alibaba.dubbo.common.utils.ConfigUtils; +import com.alibaba.dubbo.config.spring.initializer.DubboApplicationListener; import com.alibaba.dubbo.container.Container; import org.springframework.context.support.ClassPathXmlApplicationContext; @@ -44,6 +45,7 @@ public void start() { configPath = DEFAULT_SPRING_CONFIG; } context = new ClassPathXmlApplicationContext(configPath.split("[,\\s]+")); + context.addApplicationListener(new DubboApplicationListener()); context.start(); } From 8e822e1217a233bd1af63617e703921b5d055b45 Mon Sep 17 00:00:00 2001 From: Huxing Zhang Date: Sun, 20 May 2018 16:02:38 +0800 Subject: [PATCH 2/5] Better compatibility for ProtocolConfig. --- .../main/java/com/alibaba/dubbo/config/ProtocolConfig.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 b05d43113ec..d714e5958e9 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 @@ -461,12 +461,11 @@ public void destory() { } /** - * This method will do nothing. - * It should be deleted in the next major version, say 2.7.x. * Just for compatibility. + * It should be deleted in the next major version, say 2.7.x. */ @Deprecated public static void destroyAll() { - // DO NOTHING + DubboShutdownHook.getDubboShutdownHook().destroyAll(); } } \ No newline at end of file From eae97c9f7db5b577a6ccc01f2493445143c60e32 Mon Sep 17 00:00:00 2001 From: Huxing Zhang Date: Thu, 24 May 2018 16:28:21 +0800 Subject: [PATCH 3/5] Ensure Dubbo shutdown process to follow the spring lifecycle when running under Spring container. --- .../dubbo/bootstrap/DubboBootstrap.java | 28 +++++- .../alibaba/dubbo/config/AbstractConfig.java | 1 + .../initializer/DubboApplicationListener.java | 6 +- .../DubboApplicationListenerTest.java | 94 +++++++++++++++++++ 4 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/initializer/DubboApplicationListenerTest.java 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 index 7f65ef3b6aa..37ec8a304bc 100644 --- a/dubbo-bootstrap/src/main/java/org/apache/dubbo/bootstrap/DubboBootstrap.java +++ b/dubbo-bootstrap/src/main/java/org/apache/dubbo/bootstrap/DubboBootstrap.java @@ -33,14 +33,28 @@ public class DubboBootstrap { */ private List serviceConfigList; + /** + * Whether register the shutdown hook during start? + */ + private final boolean registerShutdownHookOnStart; + /** * The shutdown hook used when Dubbo is running under embedded environment */ private DubboShutdownHook shutdownHook; public DubboBootstrap() { + this(true, DubboShutdownHook.getDubboShutdownHook()); + } + + public DubboBootstrap(boolean registerShutdownHookOnStart) { + this(registerShutdownHookOnStart, DubboShutdownHook.getDubboShutdownHook()); + } + + public DubboBootstrap(boolean registerShutdownHookOnStart, DubboShutdownHook shutdownHook) { this.serviceConfigList = new ArrayList(); - this.shutdownHook = DubboShutdownHook.getDubboShutdownHook(); + this.shutdownHook = shutdownHook; + this.registerShutdownHookOnStart = registerShutdownHookOnStart; } /** @@ -54,7 +68,13 @@ public DubboBootstrap registerServiceConfig(ServiceConfig serviceConfig) { } public void start() { - registerShutdownHook(); + if (registerShutdownHookOnStart) { + registerShutdownHook(); + } else { + // DubboShutdown hook has been registered in AbstractConfig, + // we need to remove it explicitly + removeShutdownHook(); + } for (ServiceConfig serviceConfig: serviceConfigList) { serviceConfig.export(); } @@ -65,7 +85,9 @@ public void stop() { serviceConfig.unexport(); } shutdownHook.destroyAll(); - removeShutdownHook(); + if (registerShutdownHookOnStart) { + removeShutdownHook(); + } } /** 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 189877a6143..782215debcc 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 @@ -72,6 +72,7 @@ public abstract class AbstractConfig implements Serializable { legacyProperties.put("dubbo.consumer.check", "dubbo.service.allow.no.provider"); legacyProperties.put("dubbo.service.url", "dubbo.service.address"); + // this is only for compatibility Runtime.getRuntime().addShutdownHook(DubboShutdownHook.getDubboShutdownHook()); } 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 index 43ee49ded8c..c3d72981b98 100644 --- 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 @@ -31,7 +31,11 @@ public class DubboApplicationListener implements ApplicationListener Date: Thu, 24 May 2018 16:32:33 +0800 Subject: [PATCH 4/5] Remove unnecessary unit test code. --- .../DubboApplicationListenerTest.java | 35 ------------------- 1 file changed, 35 deletions(-) diff --git a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/initializer/DubboApplicationListenerTest.java b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/initializer/DubboApplicationListenerTest.java index 8427934b662..fdfe87ae1a9 100644 --- a/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/initializer/DubboApplicationListenerTest.java +++ b/dubbo-config/dubbo-config-spring/src/test/java/com/alibaba/dubbo/config/spring/initializer/DubboApplicationListenerTest.java @@ -56,39 +56,4 @@ private ClassPathXmlApplicationContext getApplicationContext(DubboShutdownHook h applicationContext.addApplicationListener(new DubboApplicationListener(bootstrap)); return applicationContext; } - - public static void main(String[] args) { - ThreadGroup tg = new ThreadGroup("test"); - Thread a = new Thread(tg, new Runnable() { - @Override - public void run() { - try { - Thread.sleep(1000000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }); - System.out.println(a.isDaemon()); - System.out.println(a.getThreadGroup().getName()); - a.start(); - } - - @Test - public void test() { - Thread a = new Thread(new Runnable() { - @Override - public void run() { - try { - Thread.sleep(1000000); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }); - System.out.println(a.isDaemon()); - System.out.println(a.getThreadGroup().getName()); - a.start(); - } - } From 0ecca8a5f92e305de2ed0e294a7a1efc5c97fbbc Mon Sep 17 00:00:00 2001 From: Huxing Zhang Date: Thu, 24 May 2018 18:29:06 +0800 Subject: [PATCH 5/5] Ensure we use Spring's shutdown hook for SpringContainer. --- .../src/main/java/com/alibaba/dubbo/container/Main.java | 2 +- .../com/alibaba/dubbo/container/spring/SpringContainer.java | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/Main.java b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/Main.java index 266ddc1c6f8..36eb9ae47d0 100644 --- a/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/Main.java +++ b/dubbo-container/dubbo-container-api/src/main/java/com/alibaba/dubbo/container/Main.java @@ -61,7 +61,7 @@ public static void main(String[] args) { logger.info("Use container type(" + Arrays.toString(args) + ") to run dubbo serivce."); if ("true".equals(System.getProperty(SHUTDOWN_HOOK_KEY))) { - Runtime.getRuntime().addShutdownHook(new Thread() { + Runtime.getRuntime().addShutdownHook(new Thread("dubbo-container-shutdown-hook") { @Override public void run() { for (Container container : containers) { diff --git a/dubbo-container/dubbo-container-spring/src/main/java/com/alibaba/dubbo/container/spring/SpringContainer.java b/dubbo-container/dubbo-container-spring/src/main/java/com/alibaba/dubbo/container/spring/SpringContainer.java index 05b5baec7b2..d4c03c67996 100644 --- a/dubbo-container/dubbo-container-spring/src/main/java/com/alibaba/dubbo/container/spring/SpringContainer.java +++ b/dubbo-container/dubbo-container-spring/src/main/java/com/alibaba/dubbo/container/spring/SpringContainer.java @@ -44,8 +44,10 @@ public void start() { if (configPath == null || configPath.length() == 0) { configPath = DEFAULT_SPRING_CONFIG; } - context = new ClassPathXmlApplicationContext(configPath.split("[,\\s]+")); + context = new ClassPathXmlApplicationContext(configPath.split("[,\\s]+"), false); context.addApplicationListener(new DubboApplicationListener()); + context.registerShutdownHook(); + context.refresh(); context.start(); }