Skip to content

Commit c784fa0

Browse files
committed
Merge branch 'ralf0131-graceful-shutdown-in-tomcat'
2 parents 06e5e67 + 7dac296 commit c784fa0

File tree

17 files changed

+536
-60
lines changed

17 files changed

+536
-60
lines changed

all/pom.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,13 @@
319319
<scope>compile</scope>
320320
<optional>true</optional>
321321
</dependency>
322+
<dependency>
323+
<groupId>com.alibaba</groupId>
324+
<artifactId>dubbo-bootstrap</artifactId>
325+
<version>${project.version}</version>
326+
<scope>compile</scope>
327+
<optional>true</optional>
328+
</dependency>
322329
<dependency>
323330
<groupId>com.alibaba</groupId>
324331
<artifactId>hessian-lite</artifactId>
@@ -418,6 +425,7 @@
418425
<include>com.alibaba:dubbo-serialization-fst</include>
419426
<include>com.alibaba:dubbo-serialization-kryo</include>
420427
<include>com.alibaba:dubbo-serialization-jdk</include>
428+
<include>com.alibaba:dubbo-bootstrap</include>
421429
</includes>
422430
</artifactSet>
423431
<transformers>

dubbo-bootstrap/pom.xml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<!--
2+
Licensed to the Apache Software Foundation (ASF) under one or more
3+
contributor license agreements. See the NOTICE file distributed with
4+
this work for additional information regarding copyright ownership.
5+
The ASF licenses this file to You under the Apache License, Version 2.0
6+
(the "License"); you may not use this file except in compliance with
7+
the License. You may obtain a copy of the License at
8+
9+
http://www.apache.org/licenses/LICENSE-2.0
10+
11+
Unless required by applicable law or agreed to in writing, software
12+
distributed under the License is distributed on an "AS IS" BASIS,
13+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
See the License for the specific language governing permissions and
15+
limitations under the License.
16+
-->
17+
<project xmlns="http://maven.apache.org/POM/4.0.0"
18+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
19+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
20+
<parent>
21+
<artifactId>dubbo-parent</artifactId>
22+
<groupId>com.alibaba</groupId>
23+
<version>2.6.2-SNAPSHOT</version>
24+
</parent>
25+
<modelVersion>4.0.0</modelVersion>
26+
27+
<artifactId>dubbo-bootstrap</artifactId>
28+
29+
30+
<dependencies>
31+
<dependency>
32+
<groupId>com.alibaba</groupId>
33+
<artifactId>dubbo-config-api</artifactId>
34+
<version>${project.parent.version}</version>
35+
</dependency>
36+
<dependency>
37+
<groupId>com.alibaba</groupId>
38+
<artifactId>dubbo-common</artifactId>
39+
<version>${project.parent.version}</version>
40+
</dependency>
41+
<dependency>
42+
<groupId>com.alibaba</groupId>
43+
<artifactId>dubbo-registry-api</artifactId>
44+
<version>${project.parent.version}</version>
45+
</dependency>
46+
</dependencies>
47+
</project>
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.dubbo.bootstrap;
18+
19+
import com.alibaba.dubbo.common.extension.ExtensionLoader;
20+
import com.alibaba.dubbo.common.logger.Logger;
21+
import com.alibaba.dubbo.common.logger.LoggerFactory;
22+
import com.alibaba.dubbo.config.ServiceConfig;
23+
import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
24+
import com.alibaba.dubbo.rpc.Protocol;
25+
26+
import java.util.ArrayList;
27+
import java.util.List;
28+
import java.util.concurrent.atomic.AtomicBoolean;
29+
30+
/**
31+
* A bootstrap class to easily start and stop Dubbo via programmatic API.
32+
* The bootstrap class will be responsible to cleanup the resources during stop.
33+
*/
34+
public class DubboBootstrap {
35+
36+
private static final Logger logger = LoggerFactory.getLogger(DubboBootstrap.class);
37+
38+
/**
39+
* The list of ServiceConfig
40+
*/
41+
private List<ServiceConfig> serviceConfigList;
42+
43+
/**
44+
* Has it already been destroyed or not?
45+
*/
46+
private final AtomicBoolean destroyed;
47+
48+
/**
49+
* The shutdown hook used when Dubbo is running under embedded environment
50+
*/
51+
private Thread shutdownHook;
52+
53+
public DubboBootstrap() {
54+
this.serviceConfigList = new ArrayList<ServiceConfig>();
55+
this.destroyed = new AtomicBoolean(false);
56+
this.shutdownHook = new Thread(new Runnable() {
57+
@Override
58+
public void run() {
59+
if (logger.isInfoEnabled()) {
60+
logger.info("Run shutdown hook now.");
61+
}
62+
destroy();
63+
}
64+
}, "DubboShutdownHook");
65+
}
66+
67+
/**
68+
* Register service config to bootstrap, which will be called during {@link DubboBootstrap#stop()}
69+
* @param serviceConfig the service
70+
* @return the bootstrap instance
71+
*/
72+
public DubboBootstrap regsiterServiceConfig(ServiceConfig serviceConfig) {
73+
serviceConfigList.add(serviceConfig);
74+
return this;
75+
}
76+
77+
public void start() {
78+
registerShutdownHook();
79+
for (ServiceConfig serviceConfig: serviceConfigList) {
80+
serviceConfig.export();
81+
}
82+
}
83+
84+
public void stop() {
85+
for (ServiceConfig serviceConfig: serviceConfigList) {
86+
serviceConfig.unexport();
87+
}
88+
destroy();
89+
removeShutdownHook();
90+
}
91+
92+
/**
93+
* Register the shutdown hook
94+
*/
95+
public void registerShutdownHook() {
96+
Runtime.getRuntime().addShutdownHook(shutdownHook);
97+
}
98+
99+
/**
100+
* Remove this shutdown hook
101+
*/
102+
public void removeShutdownHook() {
103+
try {
104+
Runtime.getRuntime().removeShutdownHook(shutdownHook);
105+
}
106+
catch (IllegalStateException ex) {
107+
// ignore - VM is already shutting down
108+
}
109+
}
110+
111+
/**
112+
* Destroy all the resources, including registries and protocols.
113+
*/
114+
private void destroy() {
115+
if (!destroyed.compareAndSet(false, true)) {
116+
return;
117+
}
118+
// destroy all the registries
119+
AbstractRegistryFactory.destroyAll();
120+
// destroy all the protocols
121+
ExtensionLoader<Protocol> loader = ExtensionLoader.getExtensionLoader(Protocol.class);
122+
for (String protocolName : loader.getLoadedExtensions()) {
123+
try {
124+
Protocol protocol = loader.getLoadedExtension(protocolName);
125+
if (protocol != null) {
126+
protocol.destroy();
127+
}
128+
} catch (Throwable t) {
129+
logger.warn(t.getMessage(), t);
130+
}
131+
}
132+
}
133+
}

dubbo-common/src/main/java/com/alibaba/dubbo/common/utils/ExecutorUtil.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,27 @@ public static boolean isTerminated(Executor executor) {
4343
return false;
4444
}
4545

46+
/**
47+
* Use the shutdown pattern from:
48+
* https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html
49+
* @param executor the Executor to shutdown
50+
* @param timeout the timeout in milliseconds before termination
51+
*/
4652
public static void gracefulShutdown(Executor executor, int timeout) {
4753
if (!(executor instanceof ExecutorService) || isTerminated(executor)) {
4854
return;
4955
}
5056
final ExecutorService es = (ExecutorService) executor;
5157
try {
52-
es.shutdown(); // Disable new tasks from being submitted
58+
// Disable new tasks from being submitted
59+
es.shutdown();
5360
} catch (SecurityException ex2) {
5461
return;
5562
} catch (NullPointerException ex2) {
5663
return;
5764
}
5865
try {
66+
// Wait a while for existing tasks to terminate
5967
if (!es.awaitTermination(timeout, TimeUnit.MILLISECONDS)) {
6068
es.shutdownNow();
6169
}

dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/AbstractConfig.java

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -73,18 +73,6 @@ public abstract class AbstractConfig implements Serializable {
7373
legacyProperties.put("dubbo.service.url", "dubbo.service.address");
7474
}
7575

76-
static {
77-
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
78-
@Override
79-
public void run() {
80-
if (logger.isInfoEnabled()) {
81-
logger.info("Run shutdown hook now.");
82-
}
83-
ProtocolConfig.destroyAll();
84-
}
85-
}, "DubboShutdownHook"));
86-
}
87-
8876
protected String id;
8977

9078
private static String convertLegacyValue(String key, String value) {

dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/ProtocolConfig.java

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import com.alibaba.dubbo.common.status.StatusChecker;
2222
import com.alibaba.dubbo.common.threadpool.ThreadPool;
2323
import com.alibaba.dubbo.config.support.Parameter;
24-
import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
2524
import com.alibaba.dubbo.remoting.Codec;
2625
import com.alibaba.dubbo.remoting.Dispatcher;
2726
import com.alibaba.dubbo.remoting.Transporter;
@@ -30,7 +29,6 @@
3029
import com.alibaba.dubbo.rpc.Protocol;
3130

3231
import java.util.Map;
33-
import java.util.concurrent.atomic.AtomicBoolean;
3432

3533
/**
3634
* ProtocolConfig
@@ -135,8 +133,6 @@ public class ProtocolConfig extends AbstractConfig {
135133
// if it's default
136134
private Boolean isDefault;
137135

138-
private static final AtomicBoolean destroyed = new AtomicBoolean(false);
139-
140136
public ProtocolConfig() {
141137
}
142138

@@ -149,27 +145,6 @@ public ProtocolConfig(String name, int port) {
149145
setPort(port);
150146
}
151147

152-
// TODO: 2017/8/30 to move this method somewhere else
153-
public static void destroyAll() {
154-
if (!destroyed.compareAndSet(false, true)) {
155-
return;
156-
}
157-
158-
AbstractRegistryFactory.destroyAll();
159-
160-
ExtensionLoader<Protocol> loader = ExtensionLoader.getExtensionLoader(Protocol.class);
161-
for (String protocolName : loader.getLoadedExtensions()) {
162-
try {
163-
Protocol protocol = loader.getLoadedExtension(protocolName);
164-
if (protocol != null) {
165-
protocol.destroy();
166-
}
167-
} catch (Throwable t) {
168-
logger.warn(t.getMessage(), t);
169-
}
170-
}
171-
}
172-
173148
@Parameter(excluded = true)
174149
public String getName() {
175150
return name;

dubbo-config/dubbo-config-api/src/main/java/com/alibaba/dubbo/config/RegistryConfig.java

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import com.alibaba.dubbo.common.Constants;
2020
import com.alibaba.dubbo.config.support.Parameter;
21-
import com.alibaba.dubbo.registry.support.AbstractRegistryFactory;
2221

2322
import java.util.Map;
2423

@@ -96,13 +95,9 @@ public RegistryConfig(String address) {
9695
setAddress(address);
9796
}
9897

99-
public static void destroyAll() {
100-
AbstractRegistryFactory.destroyAll();
101-
}
102-
103-
@Deprecated
104-
public static void closeAll() {
105-
destroyAll();
98+
public RegistryConfig(String address, String protocol) {
99+
setAddress(address);
100+
setProtocol(protocol);
106101
}
107102

108103
public String getProtocol() {

dubbo-config/dubbo-config-api/src/test/java/com/alibaba/dubbo/config/ProtocolConfigTest.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
package com.alibaba.dubbo.config;
1919

20-
import com.alibaba.dubbo.common.extension.ExtensionLoader;
2120
import com.alibaba.dubbo.config.mock.MockProtocol2;
2221
import com.alibaba.dubbo.rpc.Protocol;
2322
import org.junit.Test;
@@ -33,15 +32,6 @@
3332
import static org.junit.Assert.assertThat;
3433

3534
public class ProtocolConfigTest {
36-
@Test
37-
public void testDestroyAll() throws Exception {
38-
Protocol protocol = Mockito.mock(Protocol.class);
39-
MockProtocol2.delegate = protocol;
40-
ExtensionLoader<Protocol> loader = ExtensionLoader.getExtensionLoader(Protocol.class);
41-
loader.getExtension("mockprotocol2");
42-
ProtocolConfig.destroyAll();
43-
Mockito.verify(protocol).destroy();
44-
}
4535

4636
@Test
4737
public void testDestroy() throws Exception {

0 commit comments

Comments
 (0)