项目中通过jsch中的sftp实现上传下载文件。在压测过程中,由于调用到sftp,下载文件不存在时,系统不断抛出异常,内存飙升,逐渐把swap区也占满,通过top监控未发现占用内存的进程,通过查找sshd进程,发现服务器多了很多sftp的进程没有被关闭。

刚开始以为是sftp公共方法设计的有问题,每次创建连接都未释放,下面是部分代码片段

@Repository("SftpClient")
public class SftpClient { private Logger logger = LoggerFactory.getLogger(SftpClient.class);
private ThreadLocal<Session> sessionLocal = new ThreadLocal<Session>();
private ThreadLocal<ChannelSftp> channelLocal = new ThreadLocal<ChannelSftp>(); //初始化连接
public SftpClient init() {
try {
String host = SFTP_HOST;
int port = Integer.valueOf(SFTP_PORT);
String userName = SFTP_USER_NAME;
String password = SFTP_USER_PASSWORD;
Integer timeout = Integer.valueOf(SFTP_TIMEOUT);
Integer aliveMax = Integer.valueOf(SFTP_ALIVEMAX);
// 创建JSch对象
JSch jsch = new JSch();
Session session = jsch.getSession(userName, host, port);
// 根据用户名,主机ip,端口获取一个Session对象
if (password != null) {
// 设置密码
session.setPassword(password);
}
// 为Session对象设置properties
session.setConfig("StrictHostKeyChecking", "no");
if (timeout != null) {
// 设置timeout时间
session.setTimeout(timeout);
}
if (aliveMax != null) {
session.setServerAliveCountMax(aliveMax);
}
// 通过Session建立链接
session.connect();
// 打开SFTP通道
ChannelSftp channel = (ChannelSftp) session.openChannel("sftp");
// 建立SFTP通道的连接
channel.connect();
channelLocal.set(channel);
sessionLocal.set(session);
logger.debug("SSH Channel connected.session={},channel={}", session, channel);
} catch (JSchException e) {
throw new SystemException(ImageExceptionCode.FTP_SEND_ERROR);
}
return this;
} //断开连接
public void disconnect() {
ChannelSftp channel = channelLocal.get();
Session session = sessionLocal.get();
//断开sftp连接
if (channel != null) {
channel.disconnect();
logger.debug("SSH Channel disconnected.channel={}", channel);
}
//断开sftp连接之后,再断开session连接
if (session != null) {
session.disconnect();
logger.debug("SSH session disconnected.session={}", session);
}
channelLocal.remove();
sessionLocal.remove();
}
}

因为使用jsch的sftp有一个要注意的地方,当调用ChannelSftp.disconnect()断开sftp的连接之后,该连接的session还存在,这时,需要显式调用Session.disconnect()来断掉session。程序设计的时候也考虑到这一点了,公共方法设计的是没问题的,那么问题在哪呢?

调整日志级别,通过查看日志定位出问题所在,业务层在调用时,执行两次init()创建了两个sftp连接,但当遇到异常时,仅仅执行了一次disconnect()释放了第二次创建的,第一个连接一直没有得到释放。

 try {
sftpClient.init();
for(XxxDto is:list){
/*********业务代码,略*************/
try {
/*********业务代码,略*************/
try {
/*********业务代码,略*************/
} catch (Exception e) {
throw new BusinessException(XxxExceptionCode.UNDER_USERID_FAIL);
}
/*********下载业务代码,问题所在,此处抛出异常*************/
} catch (Exception e) {
throw new BusinessException( XxxExceptionCode.FTP_DOWNLOAD_LOCAL_FAIL);
}
}
} finally {
sftpClient.disconnect();
}

下载业务代码如下:

try {
sftpClient.init();
/*************下载业务代码,此处抛出异常被上层捕获,该方法创建的连接被释放,但上层的连接 enenenenene *****************/
} finally {
sftpClient.disconnect();
}

业务层的代码不规范,sftp连接在第一次初始化之后就不需要再在方法层里执行初始化了,这不但加剧了资源的消耗,而且由于在业务层有嵌套try,最里面抛出异常未将第一个连接释放就再次执行初始化,导致未释放的sftp原来越多。

将下载业务代码里的init()和disconnect()方法去掉,把sftp的连接和断开只交给上一层的业务层进行控制。

修改后的下载业务代码如下:

/*************下载业务代码,只专注业务 *****************/

附:问题排查过程中部分命令如下:

https://www.cnblogs.com/zjfjava/p/11007348.html

jsch连接sftp后连接未释放掉问题排查的更多相关文章

  1. Linux文件删除空间未释放

    当系统空间使用量过大需要清理空间或者清理某个文件时,有时会出现执行了删除命令之后磁盘空间并没有释放,很多人首次遇到该情况时会比较困惑,在考虑是不是像windows系统的回收站一样,删除只是逻辑删除到回 ...

  2. go的mgo,连接未释放问题,连接泄露。

    api启动几天后,卡住(连接失败,超时) 异常原因 mongo连接被占满,无法建立mgo连接,返回信息 查询点用端口可知,97%的连接被api项目占用. api项目的mongodb连接“泄露”,某处的 ...

  3. “ping”命令的原理就是向对方主机发送UDP数据包,HTTP在每次请求结束后都会主动释放连接,因此HTTP连接是一种“短连接”

    Socket  是一套建立在TCP/IP协议上的接口不是一个协议 应用层:  HTTP  FTP  SMTP  Web 传输层:  在两个应用程序之间提供了逻辑而不是物理的通信(TCP  UDP) T ...

  4. 【转】Linux下tcp连接断开后不释放的解决办法

    问题:在开发测试时发现断开与服务器端口后再次连接时拒绝连接. 分析:服务器上查看端口占用情况,假设端口为8888. netstat -anp |grep 8888 发现端口8888端口显示被占用(ip ...

  5. Linux下TCP连接断开后不释放的解决办法

    问题:在开发测试时发现断开与服务器端口后再次连接时拒绝连接. 分析:服务器上查看端口占用情况,假设端口为8888. netstat -anp |grep 8888 发现端口8888端口显示被占用(ip ...

  6. 关于连接sftp以及本地配置sftp的事情

    1.window下配置sftp服务器 参考:https://blog.csdn.net/zhangliang_571/article/details/45598939 下载:http://www.fr ...

  7. putty连接linux as5 输入密码后连接中断

    putty连接linux as5 输入密码后连接中断 1.修改putty首页的设置,选择“close session on exit” 为 “never”,之后发现输入密码后,“session clo ...

  8. TCP建立连接三次握手和释放连接四次握手

    TCP建立连接三次握手和释放连接四次握手     [转载]http://blog.csdn.net/guyuealian/article/details/52535294   在谈及TCP建立连接和释 ...

  9. 【转】TCP建立连接三次握手和释放连接四次握手

    在谈及TCP建立连接和释放连接过程,先来简单认识一下TCP报文段首部格式的的几个名词(这里只是简单说明,具体请查看相关教程) 序列号seq:占4个字节,用来标记数据段的顺序,TCP把连接中发送的所有数 ...

随机推荐

  1. pod健康检查(liveness probe存活探针&&readiness probe 可读性探针)

    在Kubernetes集群当中,我们可以通过配置liveness probe(存活探针)和readiness probe(可读性探针)来影响容器的生存周期.参考文档:https://kubernete ...

  2. 2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)-E. Explosion Exploit-概率+状压dp

    2018-2019 ACM-ICPC Nordic Collegiate Programming Contest (NCPC 2018)-E. Explosion Exploit-概率+状压dp [P ...

  3. 《你们都是魔鬼吗》第八次团队作业:第一天Alpha冲刺

    <你们都是魔鬼吗>第八次团队作业:Alpha冲刺 项目 内容 这个作业属于哪个课程 任课教师博客主页链接 这个作业的要求在哪里 作业链接地址 团队名称 你们都是魔鬼吗 作业学习目标 完成最 ...

  4. 圆柱模板价格计算器V1.0版本

    因很多客户需求,就做了一个初始版本的产品圆柱模板面积和价格的计算器,界面非常简单,做工粗糙,但是功能是可以运行.后期会在界面和功能上进行升级,打算出一个微信小程序版本.这个程序仅供参考. 演示地址:h ...

  5. AHOI2013 差异 和 BZOJ3879 SvT

    差异 题目描述 给定一个长度为 $n$ 的字符串 $S$,令 $T_i$ 表示它从第 $i$ 个字符开始的后缀.求 $\displaystyle \sum_{1\leqslant i<j\leq ...

  6. Mysql 日期与时间戳的相互转化

    select CURDATE(); #获取当前的日期,示例:2019-10-29 select UNIX_TIMESTAMP(CURDATE()); #将当前的时间格式转换为时间戳,示例:由2019- ...

  7. 获取当前时间减去 xx时,xx分,xx秒

    使用  datetime  模块来获取当前详细时间,并将当前时间减去或增加多少 import datetime # 当前时间减去两分钟 ctime = datetime.datetime.now() ...

  8. spring boot 集成 redis lettuce(jedis)

    spring boot框架中已经集成了redis,在1.x.x的版本时默认使用的jedis客户端,现在是2.x.x版本默认使用的lettuce客户端 引入依赖 <!-- spring boot ...

  9. zabbix基本介绍

    来源是 觅安教育 大家有兴趣可以去哔哩哔哩搜搜. Open-falcon是由小米公司开源 比如windows,linux,unix,openBSD,AIX,solaris,Mac等操作系统,都可以安装 ...

  10. noi.ac #32 快速排序归并排序应用

    \(des\) 给定长度为 \(n\) 的数组,要求翻转一段区间 \([l, r]\) 使其升序排列. 要求 \(\sum r - l + 1 <= 2e7\) \(sol\) 考虑快速排序,每 ...