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