一、问题描述
最近领导反馈某机器tcp连接数频繁告警,要我去定位下问题。接到任务后,首先分析一下问题:
1.项目上线已有1-2年,以前未曾反馈tcp连接超标的情况,会是最近上线的xx需求导致的?
2.tcp连接一般都和网络有关,且该机器的业务涉及到文件的上传和下载,会是文 件太大导致连接不释放?
3.会不会由于程序问题导致的?
二、解决思路
首先用netstat查看了一下tcp连接,确实存在大量的ESTABLISHED连接,且一直有上升的趋势,这应该就是导致tcp连接数告警的主要原因.然后用jstack查看了线程堆栈,发现有大量的Connection Thread与xxx IP正在建立连接,该ip主要用来和银行进行sftp文件传输。
打开IDE,全局搜索下使用了jsch的地方,果不其然,确实存在一个通用的文件上传与下载的服务类。
这是一个全局的初始化方法,首先根据主机名、用户名和账号来创建一个会话,然后调用它的连接方法,一切都很自然,看不出毛病。
下面来看一下文件上传的方法:
首先获取sftp的通道,然后就直接使用jsch的内部方法实现文件的上传,最后调用disconnect()方法来释放连接。第一反应,有打开连接的方法,上传成功也将连接进行了关闭,怎么
会出现连接不释放的情况呢?
接着看getChannelSftp()和disconnect()方法:
根据创建的session来获取sftp渠道,然后调用sftp的connect()方法正式建立文件的输入输出通道。当文件上传、下载完毕,将sftp的渠道关闭,很正常的逻辑。
但是在什么情况下会出现连接不释放的情况?看一下catch中的代码,当抛出了异常,系统会再次调用初始化init方法。跟进session.connect()方法,发现里面确实启动了一个名字为
Connection Thread的线程,且当调用disconnect()方法的时候 我们并没有手动的关闭掉该线程,而只是关闭了sftp通道而已,所以该线程一直都得不到释放。在init方法后,又重复调用了一次session连接方法,导致启动了两个线程。当时就认为应该是这个问题导致的。
但是究竟是什么原因会导致抛出异常而发起重连呢?这里只能做一下猜测,或许是tcp连接主动断开导致重连然后频繁创建新的线程,或许是和银行发起文件传输的时候由于网络不稳定导致tcp自动重连,但是之前的线程已经建立。
三、解决办法
最终的解决办法就是去掉init后面的session.connect()方法和在调用disconnect()方法的时候 再调用session.disconnect();jsch的具体实现是手动中断掉之前创建的connection thread,然后将线程置空。