目录
2.1 AbstractRegistryFactory.destroyAll
2.1.1 registry.destroy(ZookeeperRegistry):
2.1.2 registry.destroy(FailbackRegistry)
2.2.1 RegistryProtocol.destroy
1 Dubbo优雅停机概述
优雅停机主要应用于线上服务版本迭代的过程。如果老版本服务没有正常关闭的话会造成内存清理问题。
Dubbo优雅停机的实现依赖于JDK的ShutdownHook方法,这个方法在如下场景会被调用
- 程序正常退出
- 程序中使用System.exit()退出JVM
- 系统发生OutofMemory异常
- 使用kill pid干掉JVM进程的时候(kill -9时候是不能触发ShutdownHook生效的
2 Dubbo优雅停机实现
在AbstractConfig中的静态代码块添加了ShutdownDownHook机制
static {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
public void run() {
if (logger.isInfoEnabled()) {
logger.info("Run shutdown hook now.");
}
ProtocolConfig.destroyAll();
}
}, "DubboShutdownHook"));
}
public static void destroyAll() {
if (!destroyed.compareAndSet(false, true)) {
return;
}
// 关闭并清理注册中心
AbstractRegistryFactory.destroyAll();
ExtensionLoader<Protocol> 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);
}
}
}
2.1 AbstractRegistryFactory.destroyAll
public static void destroyAll() {
if (LOGGER.isInfoEnabled()) {
LOGGER.info("Close all registries " + getRegistries());
}
// 加锁,防止关闭多次
LOCK.lock();
try {
// 关闭所有已创建的注册中心
for (Registry registry : getRegistries()) {
try {
registry.destroy();
} catch (Throwable e) {
LOGGER.error(e.getMessage(), e);
}
}
REGISTRIES.clear();
} finally {
// 释放锁
LOCK.unlock();
}
}
2.1.1 registry.destroy(ZookeeperRegistry):
public void destroy() {
super.destroy();
try {
// 关闭zk客户端
zkClient.close();
} catch (Exception e) {
logger.warn("Failed to close zookeeper client " + getUrl() + ", cause: " + e.getMessage(), e);
}
}
2.1.2 registry.destroy(FailbackRegistry)
public void destroy() {
if (!canDestroy()){
return;
}
// 首先要明白FailbackRegistry的核心就在于失败重试,所以这一层的关闭只要关闭retryFuture就可以
super.destroy();
try {
retryFuture.cancel(true);
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
}
2.1.3 通用destroy
public void destroy() {
if (!destroyed.compareAndSet(false, true)) {
return;
}
if (logger.isInfoEnabled()) {
logger.info("Destroy registry:" + getUrl());
}
// 移除内存中已经注册的服务
Set<URL> destroyRegistered = new HashSet<URL>(getRegistered());
if (!destroyRegistered.isEmpty()) {
for (URL url : new HashSet<URL>(getRegistered())) {
if (url.getParameter(Constants.DYNAMIC_KEY, true)) {
try {
//从已注册的列表中移除该URL
unregister(url);
if (logger.isInfoEnabled()) {
logger.info("Destroy unregister url " + url);
}
} catch (Throwable t) {
logger.warn("Failed to unregister url " + url + " to registry " + getUrl() + " on destroy, cause: " + t.getMessage(), t);
}
}
}
}
// 取消所有的服务订阅
Map<URL, Set<NotifyListener>> destroySubscribed = new HashMap<URL, Set<NotifyListener>>(getSubscribed());
if (!destroySubscribed.isEmpty()) {
for (Map.Entry<URL, Set<NotifyListener>> entry : destroySubscribed.entrySet()) {
URL url = entry.getKey();
for (NotifyListener listener : entry.getValue()) {
try {
//将listener从订阅者对应的listener集合中移除(监听的服务变更将不再进行通知)
unsubscribe(url, listener);
if (logger.isInfoEnabled()) {
logger.info("Destroy unsubscribe url " + url);
}
} catch (Throwable t) {
logger.warn("Failed to unsubscribe url " + url + " to registry " + getUrl() + " on destroy, cause: " + t.getMessage(), t);
}
}
}
}
}
梳理一下AbstractRegistryFactory.destroyAll方法(没有先后顺序关系):
1 关闭zk客户端
2 由于服务注册zk的时候创建的临时节点,这些临时节点也会删除
3 关闭retryFuture
4 移除内存中已经注册的服务
5 取消所有的服务订阅
2.2 protocol.destroy
Protocol的实现类中主要有两个,RegistryProtocol和DubboProtocol
2.2.1 RegistryProtocol.destroy
public void destroy() {
List<Exporter<?>> exporters = new ArrayList<Exporter<?>>(bounds.values());
for (Exporter<?> exporter : exporters) {
exporter.unexport();
}
bounds.clear();
}
DubboExporter.unexport
public void unexport() {
super.unexport();
// 把内存中的export移除
exporterMap.remove(key);
}
AbstractExporter.unexport
public void unexport() {
if (unexported) {
return;
}
unexported = true;
getInvoker().destroy();
}
DubboInvoker.destroy
public void destroy() {
if (super.isDestroyed()) {
return;
} else {
// 加锁,防止并发destroy
destroyLock.lock();
try {
if (super.isDestroyed()) {
return;
}
// 关闭invoker和exchange
super.destroy();
if (invokers != null) {
invokers.remove(this);
}
for (ExchangeClient client : clients) {
try {
client.close(getShutdownTimeout());
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
}
} finally {
destroyLock.unlock();
}
}
}
注册协议只关心跟注册有关的内容,而Exporter和Invoker都是RegistryProtocol下层的内容,所以在调用注册协议关闭服务的时候会将其下的Exporter和Invoker都关闭掉。
2.2.2 DubboProtocol.destroy
public void destroy() {
//关停所有的Server,作为provider将不再接收新的请求
for (String key : new ArrayList<String>(serverMap.keySet())) {
ExchangeServer server = serverMap.remove(key);
if (server != null) {
try {
if (logger.isInfoEnabled()) {
logger.info("Close dubbo server: " + server.getLocalAddress());
}
// HeaderExchangeServer中会停止发送心态的任务,关闭channel
server.close(getServerShutdownTimeout());
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
}
}
//关停所有的Client,作为consumer将不再发送新的请求
for (String key : new ArrayList<String>(referenceClientMap.keySet())) {
ExchangeClient client = referenceClientMap.remove(key);
if (client != null) {
try {
if (logger.isInfoEnabled()) {
logger.info("Close dubbo connect: " + client.getLocalAddress() + "-->" + client.getRemoteAddress());
}
// HeaderExchangeClient中会停止发送心态的任务,关闭channel
client.close(getServerShutdownTimeout());
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
}
}
//对于幽灵客户端的处理逻辑暂时先忽略
for (String key : new ArrayList<String>(ghostClientMap.keySet())) {
ExchangeClient client = ghostClientMap.remove(key);
if (client != null) {
try {
if (logger.isInfoEnabled()) {
logger.info("Close dubbo connect: " + client.getLocalAddress() + "-->" + client.getRemoteAddress());
}
client.close(getServerShutdownTimeout());
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
}
}
stubServiceMethodsMap.clear();
super.destroy();
}
梳理一下protocol.destroy流程
1 把内存中的export移除
2 关闭RegistryProtocol管理的invoker和exchange
3 关停所有的Server,作为provider将不再接收新的请求
4 关停所有的Client,作为consumer将不再发送新的请求