netty客户端引发的线程血案(一)

来源:互联网 发布:avsow永久域名 编辑:程序博客网 时间:2024/06/07 02:10

netty客户端引发的线程血案(一)

前言

近日,在某个项目发现线程数量持续暴涨,最后OOM的问题,开发人员很头疼,刚好来问我,就协助分析了一下,观察服务器状态,CPU使用者正常,内存消耗持续增加,socket数量正常,通过jstack看,线程数量持续增加,大量线程处于epollWait函数调用中,线程状态是RUNNABLE,线程持续增加,很不正常,了解了项目的情况,发现新增了一个功能,就是使用了esl-client来连接freeswitch服务器,用来检测freeswitch服务器的状态,检测的方式是启动一个定时任务,每间隔一定时间就连接freeswitch,检测一下,服务器是否存活,和freeswitch之间的连接是通过netty进行连接。出问题的netty-client版本为3.10.5,而在老的netty-client 3.2.4版本上面不存在该问题,本文主要描述如何基于3.10.5解决该问题,后续文章讲解二者的差异。

环境

es-client:0.9.3
jdk 7
netty-client:3.10.5

问题分析

通过前面的描述怀疑是部分资源没释放导致,这里上一下图,看一下当时的JMX的截图:
这里写图片描述
前半段曲线是抓取的正常情况的线程统计,后半段是出问题的线程统计曲线,可以看到线程数量持续增加,直到进程OOM,而且增加的很有规律,跟定时任务时间刚好匹配,每间隔一定时间,增加一定数量的线程。
es-client是freeswitch官方提供的,用netty实现的客户端,用来跟freeswitch通讯。
在实际的使用过程是每次定时任务执行,创建一个netty连接,然后进行处理,处理完成,结束,由于怀疑资源没有释放,就用wireshark抓了数据包,来分析一下,是否正常的断开了和服务器之间的连接。
这里写图片描述
红框表示正常的三次握手,绿框表示四次挥手关闭,两者之间是正常的网络交互,我们发现,客户端和服务器之间做完业务后,正常的关闭了,而且是服务器根据客户端请求,将客户端主动关闭,这就表示二者之间的socket已经正常结束了。
由于使用了nettyclient,怀疑是netty使用的资源没有释放导致,虽然socket关闭了,但是netty底层使用的是nio epoll,socket正常关闭失效后,句柄未从epoll相关队列移除,这才出现了,大量线程处于epollwait状态,我们看一下es-client的close代码:

    /**     * Close the socket connection     *      * @return a {@link CommandResponse} with the server's response.     */    public CommandResponse close()    {        checkConnected();        InboundClientHandler handler = (InboundClientHandler)channel.getPipeline().getLast();        EslMessage response = handler.sendSyncSingleLineCommand( channel, "exit" );        return new CommandResponse( "exit", response );    }

可以看到,client只是跟服务器做了协议层面的退出,二者交互完成socket的关闭,并未显示调用netty的资源释放和清理。
问题已经基本定位,这里,我们显示释放一下netty资源,问题解决,截图见下:
这里写图片描述

可以看到,进程正常运行,不在出现。

备注

后续,建议开发同事,修改检测机制,不用频繁创建netty客户端,可以通过心跳机制,检测服务器是否存活,效果更好,而且资源消耗比较少