解决dubbo导致tomcat无法优雅shutdown的问题

问题由来

今天运行工程时,发现停止tomcat时,java进程并不会退出,而是必须kill -9杀掉tomcat进程。 问题出现时将线程dump出来后,发现有一个非daemon的线程仍在运行。

"Hashed wheel timer #1" prio=6 tid=0x000000000ee73800 nid=0x750 waiting on condition [0x000000001383e000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
	at java.lang.Thread.sleep(Native Method)
	at org.jboss.netty.util.HashedWheelTimer$Worker.waitForNextTick(HashedWheelTimer.java:503)
	at org.jboss.netty.util.HashedWheelTimer$Worker.run(HashedWheelTimer.java:401)
	at org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108)
	at java.lang.Thread.run(Thread.java:745)

分析原因

com.alibaba.dubbo.remoting.transport.netty.NettyClient创建了一个NioClientSocketChannelFactory,而NioClientSocketChannelFactory在构造时又会创建一个NioClientBossPool,NioClientBossPool在构造时又会创建一个HashedWheelTimer,而HashedWheelTimer创建时使用的是Executors.defaultThreadFactory(),这个线程工厂创建的线程是非daemon的,因此必须调用NioClientSocketChannelFactory的releaseExternalResources方法才可以优雅地停止这些非daemon线程。而dubbo出于规避netty的一个bug

// 因ChannelFactory的关闭有DirectMemory泄露,

// 采用静态化规避 https://issues.jboss.org/browse/NETTY-424

privatestaticfinal ChannelFactory channelFactory = new NioClientSocketChannelFactory(Executors.newCachedThreadPool(new NamedThreadFactory("NettyClientBoss", true)),

Executors.newCachedThreadPool(new NamedThreadFactory("NettyClientWorker", true)),

Constants.DEFAULT_IO_THREADS);

, 所以注释掉了NettyClient里的doClose方法里的逻辑

@Override
protected void doClose() throws Throwable {
    /*try {
        bootstrap.releaseExternalResources();
    } catch (Throwable t) {
        logger.warn(t.getMessage());
    }*/
}

而采用注册shutdownhook的方式进行资源的释放

static {
    Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
        public void run() {
            if (logger.isInfoEnabled()) {
                logger.info("Run shutdown hook of netty client now.");
            }

            try {
                channelFactory.releaseExternalResources();
            } catch (Throwable t) {
                logger.warn(t.getMessage());
            }
        }
    }, "DubboShutdownHook-NettyClient"));
}

但这个方案其实并不能释放Netty的资源,正常关闭java进程时,因为有非daemon线程存在,所以shutdownhook并不会执行,这就是个死循环。

解决方案

最后使用反射解决了此问题。

//先释放dubbo所占用的资源
ProtocolConfig.destroyAll();
//用反射释放NettyClient所占用的资源, 以避免不能优雅shutdown的问题
releaseNettyClientExternalResources();

private void releaseNettyClientExternalResources() {
	try {
		Field field = NettyClient.class.getDeclaredField("channelFactory");
		field.setAccessible(true);
		ChannelFactory channelFactory = (ChannelFactory) field.get(NettyClient.class);
		channelFactory.releaseExternalResources();
		field.setAccessible(false);
		LOGGER.info("Release NettyClient's external resources");
	} catch (Exception e){
		LOGGER.error("Release NettyClient's external resources error", e);
	}
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java学习网

Java Web Response对象的27个方法及状态码

response表示HttpServletResponse对象,主要将JSP容器处理后的结果传回到客户端。 ? 网络配图 1、void addCookie(...

53970
来自专栏java一日一条

Guava - EventBus(事件总线)

Guava在guava-libraries中为我们提供了事件总线EventBus库,它是事件发布订阅模式的实现,让我们能在领域驱动设计(DDD)中以事件的弱引用...

19020
来自专栏为数不多的Android技巧

Android 插件化原理解析——插件加载机制

上文 Activity生命周期管理 中我们地完成了『启动没有在AndroidManifest.xml中显式声明的Activity』的任务;通过Hook AMS和...

26010
来自专栏大内老A

WCF技术剖析之十八:消息契约(Message Contract)和基于消息契约的序列化

在本篇文章中,我们将讨论WCF四大契约(服务契约、数据契约、消息契约和错误契约)之一的消息契约(Message Contract)。服务契约关注于对服务操作的描...

40650
来自专栏IT技术精选文摘

ZooKeeper 分布式锁实现

18620
来自专栏Java3y

JDBC【数据库连接池、DbUtils框架、分页】

1.数据库连接池 什么是数据库连接池 简单来说:数据库连接池就是提供连接的。。。 为什么我们要使用数据库连接池 数据库的连接的建立和关闭是非常消耗资源的 频繁地...

39640
来自专栏大内老A

基于CallContextInitializer的WCF扩展导致的严重问题

WCF是一个具有极高扩展度的分布式通信框架,无论是在信道层(Channel Layer)还是服务模型层(Service Model),我们都可以自定义相关组件通...

22890
来自专栏数据结构与算法

BZOJ1014: [JSOI2008]火星人prefix(splay 二分 hash)

10820
来自专栏coolblog.xyz技术专栏

Dubbo 源码分析 - 服务导出全过程解析

本篇文章,我们来研究一下 Dubbo 导出服务的过程。Dubbo 服务导出过程始于 Spring 容器发布刷新事件,Dubbo 在接收到事件后,会立即执行服务导...

14720
来自专栏知了

ijst:基于反射的 C++ JSON 反序列化库

ijst (iJsonStruct) 一个是 C++ Json 序列化/反序列化库:

35050

扫码关注云+社区

领取腾讯云代金券