目录
问题介绍1. 重启服务器2. 修改最大线程数3. 查找线程最大的java程序4. 导出问题程序的线程日志5. 找到问题代码6. 解决方案
问题介绍
测试服务器突然无法连接,ssh登录不上。只有重启才能解决。重启一天后,又连接不上了。
于是有了下面的排查过程,最终发现是有个java程序一直在创建线程,导致线程达到服务器最大数量,服务器崩溃。
1. 重启服务器
重启后,ssh连接发现下面问题 - fork faild:Cannot allocate memory
复制代码
以为是内存满了
于是,free -h,查看内存情况,还有,观察一段时间后,内存没多大变化
2. 修改最大线程数
经过各种百度,都说可以通过修改服务器的最大线程数来解决,于是我也这么干了。当时做的时候没有截图,所以下面截图是网上找的,凑合看看。
查看最大进程数 sysctl kernel.pid_max
ps -eLf | wc -l查看 进 程数
修改最大 进 程数后系统恢复 - echo 1000000 > /proc/sys/kernel/pid_max
复制代码永久生效 - echo "kernel.pid_max=1000000 " >> /etc/sysctl.conf
- sysctl -p
复制代码 3. 查找线程最大的java程序
上一步扩大了线程数量后,感觉有点不对,因为之前没有这么配置都可以正常运行,为什么突然服务器挂了呢?肯定是有程序在作怪。
于是决定找出占用线程最多的程序。回顾最近几天,服务器中只部署了几个springboot程序。问题一定出在它们之中。
查看线程数量前20的java程序- ps -Lef |awk ‘{sum[$2]++}END{for(pid in sum) print pid, sum[pid]}'|sort -nr -k 2|head -n 20
复制代码- [root@se-test-lky01 ~]# ps -Lef |awk '{sum[$2]++}END{for(pid in sum) print pid, sum[pid]}'|sort -nr -k 2|head -n 20
- 16074 3100
- 31386 1226
- 20120 1072
- 19548 985
- 9697 829
- 3005 796
- 641 344
- 19016 324
- 16924 315
- 17870 300
- 6417 293
- 8351 171
- 7332 168
- 18259 167
- 19821 161
- 16311 157
- 18433 151
- 18048 136
- 14347 104
- 2559 100
复制代码观察一段时间后,发现进程id为16074的java程序的线程数不断增长。
4. 导出问题程序的线程日志
- [root@se-test-lky01 ~]#jstack 16074 >thread_dump.log
复制代码分析日志,发现下面情况,线程数量不断增加,代码位置在FtpMonitorProcess.java:85 - "Thread-4655" #4774 prio=5 os_prio=0 tid=0x00007f84aa2fe000 nid=0xd408b waiting for monitor entry [0x00007f802b704000]
- java.lang.Thread.State: BLOCKED (on object monitor)
- at cn.cloudwalk.bat.util.http.FtpUtil.connect(FtpUtil.java:246)
- - waiting to lock <0x00000006c09c1888> (a java.lang.Class for cn.cloudwalk.bat.util.http.FtpUtil)
- at cn.cloudwalk.bat.schedule.ftp.process.FtpMonitorProcess$1.run(FtpMonitorProcess.java:85)
- at java.lang.Thread.run(Thread.java:748)
- "Thread-4654" #4773 prio=5 os_prio=0 tid=0x00007f84aa2fc000 nid=0xd408a waiting for monitor entry [0x00007f802b805000]
- java.lang.Thread.State: BLOCKED (on object monitor)
- at cn.cloudwalk.bat.util.http.FtpUtil.connect(FtpUtil.java:246)
- - waiting to lock <0x00000006c09c1888> (a java.lang.Class for cn.cloudwalk.bat.util.http.FtpUtil)
- at cn.cloudwalk.bat.schedule.ftp.process.FtpMonitorProcess$2.run(FtpMonitorProcess.java:114)
- at java.lang.Thread.run(Thread.java:748)
复制代码 5. 找到问题代码
发现这个方法每次被调用就会创建一个新的线程。而这个方法是被定时任务调用的,每10秒调用一次。
问题就出在ftp没有配置,所以线程内执行ftp操作时,线程阻塞,没能释放。若ftp可用,则不会出现线程阻塞问题。
这就是问题根源。 - private void listDeviceFiles() {
- new Thread(new Runnable() {
- @Override
- public void run() {
- logger.debug("开始获取[ftp-设备]文件...");
- try {
- String workDir = ftpConfig.getWorkdir();
- // 连接
- FTPClient ftpClient = FtpUtil.connect(ftpConfig);
- ftpClient.changeWorkingDirectory(workDir);
- ftpClient.changeWorkingDirectory(SubscribeDataTypeEnum.DEVICE_INFO.getKey().toString());
- FTPFile[] files = ftpClient.listFiles();
- for(FTPFile file : files) {
- decomposeFile(file,ftpClient);
- }
- ftpClient.logout();
- } catch (Exception e) {
- logger.error("ftp获取文件名出错:" + e.getMessage());
- }
- }
- }).start();
- }
复制代码 6. 解决方案
不建议手动创建线程,改用使用线程池。
以上为个人经验,希望能给大家一个参考,也希望大家多多支持中国红客联盟。 |