一、前言
在 『ShutdownHook- Java 优雅停机解决方案』 一文中我们聊到了 Java 实现优雅停机原理。接下来我们就跟根据上面知识点,深入 Dubbo 内部,去了解一下 Dubbo 如何实现优雅停机。
二、Dubbo 优雅停机待解决的问题
为了实现优雅停机,Dubbo 需要解决一些问题:
- 新的请求不能再发往正在停机的 Dubbo 服务提供者。
- 若关闭服务提供者,已经接收到服务请求,需要处理完毕才能下线服务。
- 若关闭服务消费者,已经发出的服务请求,需要等待响应返回。
解决以上三个问题,才能使停机对业务影响降低到最低,做到优雅停机。
三、2.5.X
Dubbo 优雅停机在 2.5.X 版本实现比较完整,这个版本的实现相对简单,比较容易理解。所以我们先以 Dubbo 2.5.X 版本源码为基础,先来看一下 Dubbo 如何实现优雅停机。
3.1、优雅停机总体实现方案
优雅停机入口类位于 AbstractConfig
静态代码中,源码如下:
static {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
public void run() {
if (logger.isInfoEnabled()) {
logger.info("Run shutdown hook now.");
}
ProtocolConfig.destroyAll();
}
}, "DubboShutdownHook"));
}
这里将会注册一个 ShutdownHook
,一旦应用停机将会触发调用 ProtocolConfig.destroyAll()
。
ProtocolConfig.destroyAll()
源码如下:
public static void destroyAll() {
// 防止并发调用
if (!destroyed.compareAndSet(false, true)) {
return;
}
// 先注销注册中心
AbstractRegistryFactory.destroyAll();
// Wait for registry notification
try {
Thread.sleep(ConfigUtils.getServerShutdownTimeout());
} catch (InterruptedException e) {
logger.warn("Interrupted unexpectedly when waiting for registry notification during shutdown process!");
}
ExtensionLoader<Protocol> loader = ExtensionLoader.getExtensionLoader(Protocol.class);
// 再注销 Protocol
for (String protocolName : loader.getLoadedExtensions()) {
try {
Protocol protocol = loader.getLoadedExtension(protocolName);
if (protocol != null) {
protocol.destroy();
}
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
}
}
从上面可以看到,Dubbo 优雅停机主要分为两步:
- 注销注册中心
- 注销所有
Protocol
3.2、注销注册中心
注销注册中心源码如下:
public static void destroyAll() {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Close all registries " getRegistries());
}
// Lock up the registry shutdown process
LOCK.lock();
try