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

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<Registry> 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<URL> destroyRegistered = new HashSet<URL>(getRegistered());
        if (! destroyRegistered.isEmpty()) {
            for (URL url : new HashSet<URL>(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<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 {
                        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<Exporter<?>> exporters = new ArrayList<Exporter<?>>(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 <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
        //export invoker,执行服务暴露
        final ExporterChangeableWrapper<T> 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<T>() {
            public Invoker<T> 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值