1、 现象
应用接口人:“又报警了,旺旺都没法输入了……” “我的妈啊,几百个异常提醒,太恐怖了……”
监控首页:一片大红
2、 监控通信框图
上图左边的代码安装在各台机器上,大概480台左右,右边webApp有两台服务器,而与Agent通信的Server只有一台服务器。
3、 排查过程
1) 挂掉点是一段Jar包的java程序,自然排查其启动的内存,以及最大内存,查看启动参数:
java -Xmx256m –Xms256m -jar../lib/gaea.env.server-0.0.1-SNAPSHOT.jar
2) 要与几百台机器通信,并且没5秒钟都有监控任务要处理,并写redis,怀疑内存太小,加大内存。
java -Xmx2048m –Xms2048m -jar../lib/gaea.env.server-0.0.1-SNAPSHOT.jar
刚开始运行,监控显示非常正常,结果运行一晚上之后,监控图如下:
查看垃圾回收情况,FGC比YGC大很多
上面两个图非常清晰说明Server端挂机不是加内存的问题了,server端中明显存在内存泄露。
3) dump内存文件分析
用mat工具打开dump的内存文件,发现NioAcceptedSocketChannel占用了98.77%内存,点击Details查看详情,
在此基本能确定连接的通道(channel)对象过多,没有释放掉;
4) 查看源代码与客户端监控通道的相关代码,调用顺序
main函数入口—>startup(),startup方法如下:
代码中阴影部分是 new EnvServerPipelineFactory(),主要代码如下:
阴影部分是new EnvServerDataHandler(),这个方法主要用来接收几百台机器的请求任务,由于这是从main函数启动时初始化的,new一个新对象应该没有问题,因为入口只有一个,并且只能同时启动一个Server,所以理论上EnvServerDataHandler应该也只会有一个。但是,用命令jmap –histo pid |grep EnvServerDataHandler 时会发现,这个对象刚开始时比较小,但是随着运行时间的加长,这个对象也在增多,甚至多达几千个,这就奇怪了!
5) 从mat的截图中也可以看到,DefaultChannelPipeline对象也应该被怀疑(该对象来自netty框架),查看 ChannelPipeline pipeline=Channels.pipeline()这句话,
可以看到,这个pipeline每一次客户端来一个请求都是return一个新的对象,从而建立一个通信管道,那么,pipelIne.addlist(“handler”,newEnvServerDataHandler());这句代码每次都new了一个EnvServerDataHandler,所以这个对象会很多,并且没能回收;
4、 处理方法
1) 由于nettey框架中每次都new了一个channel,那么应该将这句中的handler用单例实现,保证只有一个对象,
将pipelIne.addlist(“handler”,newEnvServerDataHandler())改为
pipeline.addLast("handler", EnvServerDataHandler.getEnvServerDataHandler());
其他改动代码略。
为了监控改动之后的效果,将内存设为512m,效果如下:连续运行约80小时,FGC10次,总体维持在256m以内。
2) 由于Sever挂掉影响较大,为了进一步保证稳定性,还将进行以下处理:
server部署两台;
server端将开发自监控启动程序(下周任务);