最近搞了一个专门管理服务器的小项目 使用了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全家桶
等等等等......