dubbo 优雅停机源码分析

Runtime.getRuntime().addShutdownHook

在AbstractConfig中有一块静态代码块:

 static {
        Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {//添加一个jvm关闭执行的钩子,执行清理工作
            public void run() {
                if (logger.isInfoEnabled()) {
                    logger.info("Run shutdown hook now.");
                }
                ProtocolConfig.destroyAll();
            }
        }, "dubboShutdownHook"));
    }


 Runtime.getRuntime().addShutdownHook的方法的意思就是在jvm中增加一个关闭的钩子,当jvm关闭的时候,会执行系统中已设置的所有通过addShutdownHook添加的钩子,当系统执行完这些钩子后,jvm才会关闭。

ProtocolConfig.destroyAll

    /** 关闭注册中心,释放协议*/
    public static void destroyAll() {
    	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);
            }
        }
    }

destroyAll方法中会主要是两个工作,一个是执行AbstractRegistryFactory.destroyAll,另一个执行所有Protocol实现者的destroy方法。

 

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();
        }
    }

 

 /**
     * 获取所有注册中心
     * 
     * @return 所有注册中心
     */
    public static Collection getRegistries() {
        return Collections.unmodifiableCollection(REGISTRIES.values());//得到Map的镜像,防止数据被修改
    }


AbstractRegistryFactroy.destroyAll先通过getRegistries获取注册中心,然后调用Registry实现者的destroy方法,一般在使用zookeeper作为注册中心时,会执行到ZookeeperRegistry.destroy方法。

    public void destroy() {
        super.destroy();
        try {
        	new Thread("zkClient-delay-shutdown"){//等所有zk操作完成,再释放zkClient
        		public void run(){
        			try {
        				synchronized (object) {
        					object.wait(1000);
						}
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
        			logger.info(">>>>>>>>>>>>>>>>>start shutdown zk client!");
        			zkClient.close();
        		}
        	}.start();
//            zkClient.close();//断开zk客户端与zk的连接
        } catch (Exception e) {
            logger.warn("Failed to close zookeeper client " + getUrl() + ", cause: " + e.getMessage(), e);
        }
//        System.out.println("延迟1s关闭zk连接!");
    }

 

它先调用父类FailbackRegistry的destroy方法,再执行AbstractZookeeperClient.close方法。

FailbackRegistry.destroy

@Override
    public void destroy() {
        super.destroy();
        try {
            retryFuture.cancel(true);//强制终止重试线程执行
        } catch (Throwable t) {
            logger.warn(t.getMessage(), t);
        }
    

在这里,还是先调用父类AbstractRegistry的destroy方法,然后把与注册中心交互失败而重连的线程关闭了。

AbstractRegistry.destroy

    public void destroy() {
        if (logger.isInfoEnabled()){
            logger.info("Destroy registry:" + getUrl());
        }
        //销毁注册数据
        Set destroyRegistered = new HashSet(getRegistered());
        if (! destroyRegistered.isEmpty()) {
            for (URL url : new HashSet(getRegistered())) {
                if (url.getParameter(Constants.DYNAMIC_KEY, true)) {//如果是动态模式管理
                    try {
                        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> destroySubscribed = new HashMap>(getSubscribed());
        if (! destroySubscribed.isEmpty()) {
            for (Map.Entry> entry : destroySubscribed.entrySet()) {
                URL url = entry.getKey();
                for (NotifyListener listener : entry.getValue()) {
                    try {
                        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);
                    }
                }
            }
        }
    }

这里不是真正的从注册中心中删除注册和订阅数据,只是把这些缓存在内存中的数据清除掉。现在再回到ZookeeperRegistry.destroy方法。

 

ZookeeperRegistry.destroy

    public void destroy() {
        super.destroy();
        try {
        	new Thread("zkClient-delay-shutdown"){//cuihs,等所有zk操作完成,再释放zkClient
        		public void run(){
        			try {
        				synchronized (object) {
        					object.wait(1000);
						}
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
        			logger.info(">>>>>>>>>>>>>>>>>start shutdown zk client!");
        			zkClient.close();
        		}
        	}.start();
//            zkClient.close();//断开zk客户端与zk的连接
        } catch (Exception e) {
            logger.warn("Failed to close zookeeper client " + getUrl() + ", cause: " + e.getMessage(), e);
        }
//        System.out.println("延迟1s关闭zk连接!");
    }


调用zkClient.close会在延迟1s后进行关闭,先看一个这个zkClient.close方法。

AbstractZookeeperClient.close

public void close() {
		if (closed) {
			return;
		}
		closed = true;
		try {
			doClose();
		} catch (Throwable t) {
			logger.warn(t.getMessage(), t);
		}
	}

它最终会调用ZkclientZookeeperClient.doClose方法,这个方法会调用org.I0Itec.zkclient.ZkClient的close方法,关闭到与zookeeper的连接。

至此ProtocolConfig.destroyAll中AbstractRegistryFactory.destroyAll方法逻辑分析完了。该分析Protocol.destroy,这里我几关心RegistryProtocol和DubboProtocol

 

RegistryProtocol.destroy

public void destroy() {
        List> exporters = new ArrayList>(bounds.values());
        for(Exporter exporter :exporters){
            exporter.unexport();
        }
        bounds.clear();
    }


这里的bounds是一个Map,key是providerurl,value是exporter。exporter.unexport这里只分析DubboExporter.unexporter

 

DubboExporter.unexport

 

    @Override
    public void unexport() {
        super.unexport();
        exporterMap.remove(key);
    }

先调用父类AbstractExporter的unexport方法,然后从缓存exporterMap中移除这个exporter。

 

AbstractExporter.unexport

public void unexport() {
        if (unexported) {
            return ;
        }
        unexported = true;
        getInvoker().destroy();
    }

这里最终会调用invoker的destroy方法,这里以DubboInvoker为例会析。

    public void destroy() {
        //防止client被关闭多次.在connect per jvm的情况下,client.close方法会调用计数器-1,当计数器小于等于0的情况下,才真正关闭
        if (super.isDestroyed()){
            return ;
        } else {
            //dubbo check ,避免多次关闭
            destroyLock.lock();
            try{
                if (super.isDestroyed()){
                    return ;
                }
                super.destroy();
                if (invokers != null){
                    invokers.remove(this);
                }
                for (ExchangeClient client : clients) {
                    try {
                        client.close();
                    } catch (Throwable t) {
                        logger.warn(t.getMessage(), t);
                    }
                }
                
            }finally {
                destroyLock.unlock();
            }
        }
    }


除了防止多线程的部分外,这里还是先调用父类AbstractInvoker的在destroy方法如下,然后调用ExchangeClient.close方法关闭调用连接。

    public void destroy() {
        if (isDestroyed()) {
            return;
        }
        destroyed = true;
        setAvailable(false);
    }

 

由于后面的逻辑很多都与netty相关,所以我们先分析到这里。

 

 

ServiceBean.destroy

类这个类实现了Disposable接口,所以在停止服务时,会调用它的destroy方法,这里我做了一些修改,因为这里还要操作zookeeper,所以在ZookeeperRegistry.destroy中是延迟关闭zookeeper连接的。

    public void destroy() throws Exception {
    	logger.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ServiceBean destroy!!"+beanNum.get());
        unexport();
        if(beanNum.decrementAndGet()<1){
        	logger.info(">>>>>>>>>>>>>>>>>>>>>>>All ServiceBean have destroyed! close zookeeperClient.");
	        synchronized (ZookeeperRegistry.object) {
				ZookeeperRegistry.object.notify();
			}
	        Thread.sleep(100);//等zookeeper client关闭完成
        }
    }


需要注意的是这里的export会调用到RegistryProtocol中返回的Exporter

public  Exporter export(final Invoker originInvoker) throws RpcException {
        //export invoker,执行服务暴露
        final ExporterChangeableWrapper exporter = doLocalExport(originInvoker);
        //registry provider
        final Registry registry = getRegistry(originInvoker);
        final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
        registry.register(registedProviderUrl);
        // 订阅override数据
        // FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为subscribed以服务名为缓存的key,导致订阅信息覆盖。
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
        final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl);
        overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
        registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
        //保证每次export都返回一个新的exporter实例
        return new Exporter() {
            public Invoker getInvoker() {
                return exporter.getInvoker();
            }
            public void unexport() {
            	try {
            		exporter.unexport();
            	} catch (Throwable t) {
                	logger.warn(t.getMessage(), t);
                }
                try {
                	registry.unregister(registedProviderUrl);
                } catch (Throwable t) {
                	logger.warn(t.getMessage(), t);
                }
                try {
                	overrideListeners.remove(overrideSubscribeUrl);
                	registry.unsubscribe(overrideSubscribeUrl, overrideSubscribeListener);
                } catch (Throwable t) {
                	logger.warn(t.getMessage(), t);
                }
            }
        };
    }


最后调用到AbstractRegistry->FailbackRegistry的unregistry至此分析完成。

 

最后欢迎大家访问我的个人网站:1024s

你可能感兴趣的:(秒扒Dubbo)