记录一次jsch访问sftp多线程死锁的问题

     最近搞了一个专门管理服务器的小项目 使用了jsch, 发现查询文件夹偶尔就不返回然后多点几次文件列表就不会返回了, 准备打开服务器看日志然后就发现cpu就飙升到100%, 看了看日志会爆奇怪的错误 Caused by: com.jcraft.jsch.JSchException: 4 , 然后搞了一段时间发现是多线程的问题

1. 问题分析

    遇到死锁不要慌 先看看到底是哪一个进程锁住了 先使用 top 命令查找cpu占用最高的进程

    再使用使用jstack查找阻塞的线程 命令: jstack -l 11278 | grep nid -A 30 > thread.log && cat thread.log  | grep BLOCKED -A 30

这时候看到了多个地方阻塞, 去重之后发现有两个方法在互相等待


 一个是查询文件夹树, 一个是查询文件列表的方法 他们底层实现都是使用的 channelSftp的ls方法

看到这里我大概知道原因出在哪里了

 

发现这两个接口是几乎同时调用的,但是代码里面用的channel是同一个 可能是因为并发将jsch底层阻塞住了 

 我这里使用同一个执行器的原因是因为不想二次连接服务器然后就将它缓存了起来, 这里的SftpExecutor是把jsch的api2次封装了一下 

2. 思考解决方案 1.0 重新创建

我直接不从 sftpBasicExecutorHolder.getBasicExecutor 这个方法获取了每次都新创建一个执行器

改造前:

 改造后: 

 这样改完发现确实是可以的, 但是这样真的是太慢了 每次都会重新建立连接, 影响性能

3. 思考解决方案 2.0 加锁, 前端await 同步promise

 直接给executor加上锁, 然后改一改前端的调用逻辑 发现这样就好了 只不过性能稍微有一点点影响, 但是比每次创建的方案要快不少

4. 总结

1.  Caused by: com.jcraft.jsch.JSchException: 4 这个问题造成的原因可能是因为同步执行没有加锁, 内部的变量访问的某些状态和变量可能被同时修改了才会报这个错导致文件列表不会返回

2. 在多线程模型的情况下并发操作一定要加锁, 无论是synchronized 还是juc包下的工具都是可以的

3. 正在分析排查问你题的时候要学会使用工具 如: jdk/bin目录下的工具(jps jmap jstat jstack) mat等

5. 项目地址 

项目路径在git上面有兴趣的朋友也可以看一看 有很多有趣的功能和代码 很多功能都在开发中 毕竟前后端都是自己写会有点慢

前端使用的vue + ant        后端使用的springboot全家桶

gitee   github

 

 等等等等......

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JSch是一个Java库,用于在Java程序中连接和操作SFTP服务器。通过JSch,你可以使用SFTP协议在本地和远程服务器之间传输文件。下面是一个使用JSch连接SFTP服务器的示例代码: ```java JSch jsch = new JSch(); Session session = jsch.getSession("username", "hostname", port); session.setPassword("password"); session.setConfig("StrictHostKeyChecking", "no"); session.connect(); Channel channel = session.openChannel("sftp"); channel.connect(); ChannelSftp sftp = (ChannelSftp) channel; // 在这里可以执行SFTP操作,比如上传、下载、删除文件等 channel.disconnect(); session.disconnect(); ``` 以上代码中,你需要替换`username`、`hostname`、`port`和`password`为你实际的SFTP服务器的用户名、主机名、端口和密码。通过调用`session.connect()`方法建立与服务器的连接,然后通过`session.openChannel("sftp")`打开SFTP通道,最后通过`channel.connect()`连接到SFTP服务器。你可以在这个连接上执行各种SFTP操作,比如上传、下载、删除文件等。最后,通过`channel.disconnect()`和`session.disconnect()`关闭连接。 如果你想读取服务器上指定路径下的所有文件,可以使用以下代码: ```java Vector<ChannelSftp.LsEntry> files = sftp.ls("/path/to/directory"); for (ChannelSftp.LsEntry file : files) { String filename = file.getFilename(); boolean isDirectory = file.getAttrs().isDir(); System.out.println(filename + " is a directory: " + isDirectory); } ``` 以上代码中,你需要将`/path/to/directory`替换为你想要读取的目录路径。通过调用`sftp.ls()`方法可以获取指定路径下的所有文件和文件夹的信息,然后通过遍历`files`列表可以获取每个文件的名称和是否是文件夹。 请注意,使用JSch连接SFTP服务器需要添加相应的依赖。你可以在你的项目的`pom.xml`文件中添加以下依赖: ```xml <dependency> <groupId>com.jcraft</groupId> <artifactId>jsch</artifactId> <version>0.1.53</version> </dependency> ``` 这样,你就可以使用JSch库连接SFTP服务器并执行相应的操作了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值